Java. Parte III

ArticleCategory: [Artikel Kategorie]

Software Development

AuthorImage:[Bild des Autors]

[Photo of the Author]

AuthorName:[Name des Autors]

Jose M Fernández

AboutTheAuthor:[Über den Autor]

Diplomado en informática, en la especialidad de planificación y control de sistemas informáticos por la universidad de Málaga a finales de los años 80. Desde entonces trabajó primero como programador y después como analista de aplicaciones en empresas siempre relacionadas con la Administración. Siempre he trabajado con grandes sistemas (y profesionalmente sigo haciéndolo) pero hace año y medio por casualidad tropecé con Linux y descubrí que en este mundo la libertad se escribe con letras MAYÚSCULAS.

Abstract:[Zusammenfassung]

Con esta última entrega daré por terminada la primera serie de artículos dedicados a Java (I y II ), en el que he intentado dar una visión global y definir los conceptos básicos del lenguaje. Después de este artículo podremos comenzar el estudio de los aspectos más novedosos de Java, siempre (y que nadie se despiste) desde el prisma de GNU-Linux. Pero todavía nos queda un buen trecho para cerrar esta primera fase.

ArticleIllustration:[Titelbild des Artikels]

[Ilustration]

ArticleBody:[Der eigentliche Artikel]

Introducción

El artículo anterior finalizó con el tema de las clases, más concretamente con la Herencia y desde este mismo punto arrancaremos este tercer artículo para concluir el tema. Continuaremos con otros temas no menos importantes en Java como el control de excepciones y la programación concurrente además de la entrada/salida básica. Finalizaremos con un repaso general a los paquetes básicos de Java entre los que podemos destacar Java.net para todos los trabajos en red, Java.awt y Java.Applet para los entornos gráficos y applets tal vez el aspecto de Java más conocido.

CLASES. PAQUETES. INTERFACES. CLASES ABSTRACTAS. SUPER. THIS. STATIC. MATRICES. STRING

Las clases abstractas se utilizan para crear superclases que sólo definen formas generalizadas. Formas que serán compartidas por todas las subclases, dejando a cada una de estas la tarea de crear los detalles. La superclase determina la naturaleza de los métodos que la subclase deben implementar.
 
abstract tipo nombre (lista_de_parametros);

Se puede hacer que ciertos métodos sean sobrescritos por las subclases utilizando el identificador de tipo abstracto. Es responsabilidad de la subclase implementarlo ya que no posee ninguna implementación de la superclase. La forma general:

Cualquier clase que contenga uno o más métodos abstractos se tiene que declarar como abstract, para declararla simplemente, se utiliza la palabra abstract al principio de la declaración de la clase. No se puede crear instancias de estos tipos de clases. Aunque sí pueden ser utilizadas para crear referencias a objetos. Es posible crear una referencia de una clase abstracta para que pueda ser utilizada como referencia a un objeto de una subclase. Gracias a este mecanismo se puede decidir en tiempo de ejecución cuál de las versiones de un método sobrescrito se debe ejecutar.

Para que un método haga referencia al objeto que lo invocó, Java define una palabra clave this, es siempre una referencia al objeto sobre el que ha sido llamado el método. Se utiliza siempre que se quiera hacer una referencia a un objeto del tipo de la clase actual.

Para definir métodos de una clase que puedan ser utilizados por sí mismo sin referirse a una instancia específica se precede en la declaración del método con la palabra clave static. El método puede ser referenciado sin haber creado ningún objeto de esa clase. El ejemplo más común es en la declaración de main() ya que es llamado sin que exista ningún objeto.

Super se puede utilizar para dos cosas: para llamar al constructor de la superclase y para acceder a un miembro de la superclase que ha sido ocultado por un miembro de la subclase.

Paquetes: son contenedores de clases que se utilizan para dividir en compartimentos el espacio de nombres de clases, los paquetes son un componente básico de los programas, para definirlos hay que utilizar el comando package como primera sentencia del archivo fuente. Cualquier clase que se defina dentro de este archivo pertenece a ese paquete, estos definen un conjunto de espacios de nombres particionados en los que se almacenan las clases.
 
Import paquete1. [paquete2].(nombre_clase | *) ; 

Todas las clases estándar están almacenadas en algún paquete con nombre Java, con la sentencia import importamos paquetes enteros en nuestros programas y así una clase incluida en el paquete importado puede ser referenciada directamente. El formato general se la sentencia import es:
 
[public ] nombre_interface{ 
tipo metodo1 (parámetros);
tipo variable_final1=valor; 
tipo metodo2 (parámetros);
tipo variable_final2=valor; ... 
tipo metodo (parámetros);
tipo variable_finaln=valor; 
}

Interfaces: En las clases que definimos como interfaces especificamos qué es lo que hace, pero no cómo lo hace. Tienen el mismo aspecto que una clase pero sin variables de instancias y con métodos declarados sin cuerpos. Definiendo una interfaz cualquier clase puede implementarla. Este mecanismo permite implementar totalmente el polimorfismo "una interfaz múltiples métodos". Las interfaces están es jerarquías distintas de las de las clases, por lo que es posible que varias clases sin relación implementen la misma interfaz. El formato general de una interfaz sería:

Observa que los métodos declarados no tienen cuerpo, son métodos abstractos ya que no se pueden implementar en la declaración de una interfaz, cada clase que incluye una interfaz debe implementar todos los métodos. El acceso a la interfaz es public o no se utiliza, en este último caso se utiliza el modificador de acceso por defecto y la interfaz estará solo disponible para los otros miembros de paquete en la que ha sido declarada. Si se declara public la interfaz puede ser utilizada por cualquier código.

Las variables se pueden declarar dentro de las declaraciones de la interfaz y son implícitamente "final" y static lo que indica que sólo se puede modificar la clase que las implementa y además pueden ser inicializadas con un valor constante.
 
[public] class nombre_clase [extends superclase]
[implements interfaz1, [interfaz2]....] {
//cuerpo 
}

Una vez definida la interfaz una o varias clases pueden implementarla, para ello hay que incluir la sentencia implements en la definición de la clase y crear los métodos definidos por la interfaz. El formato general será:

El formato del método implementado debe coincidir exactamente con el formato del tipo especificado en la definición de la interfaz.

Las interfaces a igual que las clases pueden utilizar la herencia, incluyendo la palabra clave extends.

Para poder realizar ejemplos de todo los que llevamos hasta ahora , estudiaremos dos clases que nos serán muy útiles la clase String y las matrices.

Matrices: Las matrices, como casi todo el mundo sabe, son un grupo de variables del mismo tipo, a las que se hace referencia con el mismo nombre. Se pueden crear matrices de cualquier tipo y pueden tener una o más dimensiones. Además las matrices no son un tipo simple de datos por lo que, por definición, todo lo que no es un tipo simple de datos es un objeto en Java, y las matrices habrá que tratarlas como tal.

La forma general de la definición de una matriz es:
 

tipo nombre_matriz [] ;
unidimensional tipo nombre_matriz[][]...[] ;
multidimensionales
Nombre_matriz = new tipo [tamaño] 

Esta declaración establece una variable tipo matriz pero realmente no existe ninguna matriz hasta que no resolvemos memoria con el operador new.

Donde tipo específica el tipo de los datos que van a ser asignados, tamaño el número de elementos de la matriz. La memoria necesaria para cada matriz la reserva dinámicamente, una vez reservada se puede acceder a un elemento específico de la misma indicando su índice entre corchetes.

La definición de una matriz se puede hacer en una sola línea:
 

tipo nombre_matriz = new tipo [tamaño]; 

String: a igual que las tablas los string son otra estructura básica para cualquier lenguaje de programación y a igual que los array los String en Java no son un tipo básico por lo que se implementa como un objeto. Dispone de un conjunto completo de operaciones que facilita su utilización.

Hay algo importante en la clase String, cuando se crea un objeto de este tipo no puede ser modificado (no se pueden modificar los carácteres que forman la cadena). Java nos permite hacerlo aunque lo que ocurre es que creamos un nuevo objeto con las modificaciones ya realizadas, esta implementación la realiza Java por eficiencia del lenguaje. Existe una clase paralela a la String llamada StringBuffer que permite ser modificada.

Como toda clase que se precie la clase String y StringBuffer tiene varios constructores:
 

String cadena = new String();  Crea una instancia vacía 
Char car[] = {'a' ,'A','b','B'} String cadena = new String(car);  Crea una instancia de String, inicializandola con los caracteres de "car"(que es una array de tipo char) 
String cadena = new String(car,i,n);  Idem anterior pero indicando la posición de inicio(i) y numero de caracteres(n) que se utiliza. 
String c1 = new String(cadena);

Si tenemos creado el string del ejemplo anterior, podemos hacer:

Con lo que las dos instancias creadas tendrán el mismo contenido.
 
String cadena = "aAbB";

Una forma muy común de crear un String es utilizar literales:

Ya que Java crea automáticamente un objeto String por cada literal de tipo cadena que haya en el programa.

Una vez creada nuestra instancia podemos realizar multitud de operaciones que corresponde con los métodos definidos en esta clase, repasaremos los más interesantes: donde c = "aAbB"
 

int length(); l = c.length(); Longitud de una cadena, número de caracteres que tiene.
int l=34; String c="tengo" + l + "años"; Concatenación de caracteres incluso con tipos que no son String.
char charAt(int pos) char ch; ch=c.charAt(1); Extrae un carácter de una cadena. En nuestro ejemplo vale "a".
Void getChars(int posin, int posfin,chardestino[],int desin); Extrae más de un carácter a la vez, posin indica la posición inicial de la subcadena, posfin la posición siguiente donde termina la subcadena, en destino[] se obtendrá los resultados copiando la subcadena en la posición dentro de destino[] que indique desin.
Char[] toCharArray(); Convierte los caracteres de un objeto String en una cadena de caracteres.
String toLowercase(); String toUpperCase(); Convierte todos los caracteres de una cadena a mayúsculas o minúsculas respectivamente.
String trim(); Devuelve el string eliminado los espacios en blanco del principio y del final.
ValueOf(); Convierte tipos de datos a String.
String substring(int posinicial); Permite extraer un trozo de una cadena de un string, posinicial indica el inicio de la subcadena.
String concat(); Concatena cadenas.
String replace(char original, char sustituto); Sustituye en la cadena que llama al método todos los caracteres que coinciden con original por sustituto.
boolean equals(object str); 
boolean equalsIgnoreCase(object str);
Copara cadenas para ver si son iguales.
Idem ignorando los caracteres mayúsculas y minúsculas.
int indexOf(int ch); int lastIndexOf(int ch); Devuelve la primera y última aparición de un carácter en una subcadena.
StringBuffer insert (int indice, String str); Inserta una cadena en otra.
StringBuffer reverse(); Invierte el orden de los caracteres de una cadena.

Ejemplo: repasaremos en este ejemplo algunos de los temas que hemos visto, en este ejemplo desarrollaremos una clase que nos sirva para contar los distintos caracteres que le enviamos en un String. La clase la instanciaremos desde otra clase principal que recibe la cadena a analizar desde la línea de comandos:
 

CÓDIGO COMENTARIOS 
Class Contar{
char La_Matriz[] = new char[128];

void Caracteres(String La_Cadena){
int Indice;
char car; 
for(Indice=0 ; Indice<La_Cadena.length() ; Indice++){
car = La_Cadena.charAt(Indice);  
++La_Matriz[car]; 
} 
}

void Resultado (){
int Indice, Resultado;
char car;
for (Indice=0;Indice < 128;Indice++){
car=(char)Indice;
Resultado =(int)La_Matriz[Indice];
If (Resultado > 0){ 
        System.out.println("del caracter " 
             + car + " hay :" 
             + Resultado
             + " Caracteres" );
} 
} 
} 
La clase contar define una matriz de caracteres de 128 ocurrencias, además se definen dos métodos:

Caracteres y resultado, los dos no devuelven ningún valor (void). El primero de ellos recibe como parámetro un string el cual vamos a analizar. Utilizamos dos métodos de la clase String vistos anteriormente:

La_cadena.length() que nos devuelve la longitud de la cadena. Y La_cadena.charAt(Indice) que extrae un carácter del String de la posición indicada por Indice.

Señalar que utilizamos car de tipo char como índice de la matriz.

El método resultado simplemente devuelve el resultado de la operación de calcular las veces que se repite en una frase cada carácter.

class Pcontar {

public static void main(String arg[]) {
String cadena = "";
int i;
for(i=0; i<arg.length ;i++){
cadena = cadena + arg[i]; } 
Contar Frases = new Contar();  
System.out.println(cadena); 
Frases.Caracteres(cadena); 
Frases.Resultado(); }
}
La clase Pcontar la utilizamos para instanciar la clase Contar y recibir la frase a analizar para enviársela vía parámetro al objeto creado. Como observamos es un programa que se ejecuta en la línea de comando y recibe parámetros por medio de un array de String arg[]. 

Concatenamos cada uno de las ocurrencias del parámetro de entrada(cada palabra de la frase esta en una ocurrencia de la matriz).

Creamos el objeto Frases del tipo Contar y llamamos a los métodos de este: Caracteres, enviando como parámetro la frase a analizar y al método Resultado para que represente en pantalla el resultado el análisis.

PROGRAMACION CONCURRENTE

La programación concurrente dentro del mismo lenguaje de programación es un concepto relativamente nuevo. Un lenguaje pionero en este tema fue Ada (1983). La arquitectura de Java admite ejecución en subprocesos múltiples.

El paradigma de la orientación a objetos y la ejecución en subprocesos permite a usuario interactuar con varios objetos simultáneamente. De quí en adelante supondré que el lector conoce los conceptos básicos de programación concurrente: hilo de ejecución, sección crítica, semáforo, recurso etc. Estos conceptos están perfectamente explicados en cualquier buen libro sobre sistemas operativos.

Existen dos formas de crear una clase que admita ejecutarse en subprocesos múltiples:

En Java todo proceso concurrente tiene un hilo principal, desde el cual se crearan el resto de los procesos del programa. Cuando el hilo principal finaliza todos los subprocesos terminan y así la ejecución del programa.

Para crear subprocesos hijos extendiendo la clase Thread:
 

 
Class nombre_clase extends Thread { 
Public void run() { 
// cuerpo de ejecucion del subproceso. 
} 
}

Para instanciar la clase y crear el subproceso concurrente con el hilo principal haremos:
 

nombre_clase nombre_instancia = new nombre_clase();

Y llamaremos al método start() que invoca al método run(), iniciando el subproceso y volviendo al hilo principal.

Si lo hacemos con la interfaz Runnable:
 

Class nombre_clase implements Runnable { 
Public void run() { 
// cuerpo subproceso 
} 
}
En el proceso principal haremos:
Nombre_clase nombre_instancia = new nombre_clase; 
New Thread(nombre_instancia).Start();

Normalmente se utilizará este último método ya que por una norma no escrita se extiende una clase si ésta va a ser mejorada o sobrescrita y éste no es el caso de la clase Thread.

Otros métodos útiles para controlar el estado y ejecución de un subproceso serán:
 

getName() Devuelve una cadena con el nombre del subproceso.
setName(String) Da un nombre a un subproceso.
currentThread() Devuelve el subproceso en ejecución.
isActive() Comprueba si el subproceso esta activo.
suspend() Suspende la ejecución de un subproceso.
resume() Reanuda la ejecución de un subproceso.
sleep(int) Suspende el ssubproceso durante int milisegundos.
yield() El subproceso cede la ejecución a otro en espera.

Cuando uno o varios subprocesos comparten un recurso, ya sea un dispositivo físico o simplemente variables y se pueden producir "condiciones de carrera" (esto es, errores de ejecución debido a una ejecución no secuencial) se debe implementar la sincronización entre los hilos para asegurar que solo uno está accediendo al recurso en un instante dado. Java proporciona un soporte único a nivel de lenguaje, utilizando el concepto de semáforo, este no es mas que un objeto que puede ser utilizado por un solo subproceso en un instante determinado. Esto se pude hacer por dos vías:

Restringiendo el acceso a un método, utilizando la palabra clave synchronized en la definición del método.
 
Class nombre_clase {
Syschronized void metodo () { 

la segunda vía sería con la sentencia syschronized, la llamada a los métodos se realiza dentro de un bloque de código sincronizado.
 

Sysnchronized (objeto){
   // sentencias que deben ser sincronizadas
}
Este segundo caso es útil ya que podemos utilizar clases de carácter general que en su definición sean sincronizadas o simplemente no dispongamos de su código fuente.

Java implementa métodos para facilitar la comunicación eficiente entre subprocesos:
 

wait() Indica al hilo en curso que abandone el semáforo (recurso sincronizado) y espere hasta que otro hilo entre en el mismo semáforo y llame a notify().
notify() Activa el primer hilo que realiza la llamada a wait()
notifyAll() Despierta a todos los subprocesos que realizan llamadas a wait().

Ejemplo:

En el siguiente programa veremos como utilizamos la concurrencia e Java, para ello creamos una clase que escribe en la salida estándar si un número es divisible por otro. El programa principal recibe como parámetro el limite de la serie y llama concurrentemente a la primera clase de tres formas para obtener los múltiplos de 2, 3 y 5 de forma concurrente.
 

CÓDIGO  COMENTARIOS 
Class Cmultiplos implements Runnable {
int limite, multiplo;
Thread t;
Cmultiplos (int h , int m){
limite = h;
multiplo = m;
t = new Thread(this, "hijo");
t.start();
}

public void run() {
for (int i = 1;i < limite + 1; i++) {
int mod = i % multiplo;
if (mod == 0){
System.out.println( i + " es multiplo de " 
                    + multiplo);
} 
}
}
}
  
Creamos la clase Cmultiplos para que pueda ser utilizada de forma concurrente utilizado en interfaz Runnable.
class Multiplos {

public static void main (String arg[]) {
int parametro=Integer.valueOf(arg[0]).intValue();
new Cmultiplos(parametro,2);
new Cmultiplos(parametro,3);
new Cmultiplos(parametro,5);
}
}
El programa principal llama a la clase en tres ocasiones enviándole parámetros distintos, con lo que se ejecutara esta clase de forma concurrente.

EXCEPCIONES EN JAVA

Java puede detectar e indicar qué errores se han producido en tiempo de ejecución. Cuando se produce un error se crea un objeto y es enviado al método que lo ha provocado, este puede elegir gestionar la excepción o no.
 
try {
// código que puede generar una excepción
} catch (excepción 1 nombre){
 // gestión excepcion1 }
} catch (excepción2 nombre){
// gestión de excepcion2 }
} finally {
//código

Para manejar excepciones lo realizaremos con la ayuda de:

Try:
crea un bloque de código donde puede producirse una excepción.
Catch:
recoge las instrucciones generadas y las trata.
Finally:
recoge el código que necesitamos que se ejecute incluso aunque se genere una excepción.
De una forma esquemática:

E/S EN JAVA

Java realiza la entrada-salida a través de flujos. Cada flujo está relacionado con un dispositivo físico. Todos los flujos se comportan de la misma forma.

La entrada-salida más habitual será la realizada con los dispositivos por defecto: la consola como salida y el teclado como entrada (ya lo hemos utilizado en algunos ejemplos anteriores).

En la cima de la jerarquía de clases de E/S se encuentra las clases InputStream y OutputStrean de las cuales se derivan el resto de las clases, la entrada y salida estándar están encapsuladas en la variables de objeto int y out de la clase System, dichas variables de objeto son de los tipos InputStrean y OutputStream respectivamente. Por lo que:
 

System.out

System.int

System.err 

Flujo de salida estándar.

Flujo de entrada estándar.

Flujo de error estándar.

La salida estándar la conocemos de ejemplos anteriores por lo que veremos la utilización de System.int con el método principal que es Read(), el formato general será:
 

In Read() throws IOException

Con este método, por cada llamada lee un único carácter y lo devuelve como un valor entero. Devuelve -1 cuando encuentra el final del flujo. Como podemos observar hay que controlar la excepción IOException.

Además de la E/S básico, el dispositivo más importante para comunicarse con el mundo exterior es el sistema de archivos. La clase File proporciona un método independiente para guardar información referente a un archivo, se utiliza para obtener modificar la información asociada a un archivo.

Los objetos File se pueden crear utilizando uno de los tres constructores siguiente (los ejemplos utilizan un archivo en /usr/local/texto/articulo.txt):
 

File (string directorio);
Ejemplo:
File dir = new File("/usr/local/texto");
Crea un objeto file basado en el directorio final.
File(String directorio, String archivo);
Ejemplo:
File Arch=new File("texto","articulo.txt");
Crea una instancia de la clase file para un archivo especifico y no para el directorio completo.
File(File obj, String archivo);
Ejemplo:
File Arch= new File(dir,"articulo.txt");
Idem anterior, pero en lugar de definir el directorio como un string se define como un objeto.

La clase file define muchos métodos, donde los más destacados pueden ser:
 

MÉTODO  DESCRIPCIÓN 
public String getName() El nombre del archivo como String, no devuelve la estructura de directorios donde se encuentra.
public String getPath() El nombre del archivo en la clase File.
public String getAbsolutePath()  El nombre del archivo, precedido de la ruta de acceso.
public String getParent() El nombre del directorio padre si existe si no null.
public boolean exists() Devuelve verdadero si el archivo existe el sistema.
public boolean isFile() Devuelve verdadero si la instancia de File es un archivo valido y normal.
public boolean canWrite() Devuelve verdadero si existe el archivo y este puede modificarse.
public boolean canRead() Devuelve verdadero si existe el fichero y este puede leerse.
public int lastModified() La última vez que se modifico el fichero.
public boolean renameto(File dest) Renombra el archivo actual con el nombre del archivo de la clase File. Devuelve true si ha tenido existo y false en caso contrario.
public boolean mkdir() Crea en la clase File el directorio relativo al directorio actual. Devuelve verdadero si el directorio se creo con existo y falso en caso contrario.
public boolean mkdirs() Idem caso anterior pero crea la estructura de directorio completa.
public String list[] Lista el contenido del directorio que se encuentra en la clase file. Devuelve una matriz con nombre de archivos menos "." y "..".

FileInputStream : crea un InputStream que se puede utilizar para leer el contenido de un archivo. Los constructores más habituales son:
 

FileInputStream(String ruta) throws FileNotFoundExcption;
FileInputStream(File obj) throws FileNotFoundException;

Donde ruta es el nombre completo del directorio de un archivo y obj es un objeto de tipo File. Si el archivo no existe genera una excepción.

Métodos definidos por fileinputstream:
 

MÉTODO  DESCRIPCIÓN 
int read() Devuelve como entero el siguiente byte de la entrada.
int read(byte bufer[]) Intenta leer hasta bufer.length bytes situándolos en bufer y devuelve el número de bytes leídos con exito.
int read (byte bufer[], int off, int len) Intenta leer len bytes situándolos en bufer comenzando en bufer[off], devuelve el número de bytes que se leyeron con exito.
int skip(long n) Omite n bytes de la entrada y devuelve el numero de bytes que se han omitido.
int available() Devuelve el número de bytes disponibles para la lectura.
void close() Cierra el origen de la entrada.
void mark(int n) Coloca una marca en el punto actual del flujo de entrada que seguirá siendo valido hasta que se lean n bytes.
void rest() Devuelve el puntero de entrada a la marca establecida con el método anterior.
boolean markSupported() Devuelve true si se admite los métodos mark() o reset().

La clase FileOutputStream crea un OutputStream que se utiliza para escribir en un archivo. Sus dos constructores más habituales son:
 

FileOutputStream (String ruta) throws IOException;

FileOutputStream(File obj) throws IOException;

Donde ruta es nuevamente el directorio completo de un archivo y obj un objeto de tipo File, puede lanzar una excepción IOException. Los métodos más interesantes que define esta clase son:
 

MÉTODO  DESCRIPCIÓN 
void write(int b) Escribe un único byte en un fichero de salida.
void write(byte buffer[]) Escribe una matriz completa de bytes en un flujo de salida
void write(byte bufer[], int n, int lon) Escribe lon bytes de la matriz bufer, comenzando a partir de bufer[n].
void flush() Inicializa la salida.
void close() Cierra el flujo de salida.

Java permite los flujos con bufer, esto permite realizar las operaciones de E/S con varios bytes a la vez incrementándose el rendimiento. Se define las clases:
 

BufferdInputStream

BufferdOutputStream

RandomAccessFile
 
RandomAccesFile(File obj, string modo)
RandomAccesFile(String archivo, String modo)

Ofrece múltiples métodos para interacciones con un archivo, en esta clase se aúnan todas las funciones de las clases anteriores más la posibilidad de acceder a un archivo que se encuentre en cualquier lugar y además es posible determinar con exactitud la posición del archivo desde el cual se desea recuperar los datos. Se definen los siguientes constructores:

Donde obj especifica el nombre del archivo que se desea abrir a igual que archivo pero este como un String. En ambos casos modo determina el tipo de acceso al archivo:

r: sólo lectura

w: lectura, escritura

El método seek(long posinicial) se utiliza para establecer la posición actual del puntero dentro del archivo.

Ejemplo :
En el siguiente ejemplo veremos como accedemos a un fichero secuencial, leeremos carácter a carácter y como en un ejemplo anterior calcularemos el numero de caracteres que hay distintos en el fichero:
 

CÓDIGO  COMENTARIOS 
import java.io.*;
class Fichero {

public static void main(String args[]){ 
int i, j ;
char c;
char linea[] = new char [256];
String archivo= args[0]; 
FileInputStream fichero;
try { 
fichero = new FileInputStream(archivo);
}catch (FileNotFoundException e) {
System.out.println("archivo no encontrado");
Return;
} 
try {
do {
i = fichero.read();
if (i != -1)
++linea[i];
} while (i != -1);
}catch(IOException e){
System.out.println("error e/s");
return;
}
try {
fichero.close();
}catch(IOException e){
System.out.println("error e/s");
return;
}
for (j=0;j<256;j++) {
if (linea[j] > 0)
System.out.println("del caracter: "
                   + (char)j
                   + " hay :"
                   + (int)linea[j]);
}
}
}
Importamos el paquete java.io, para poder realizar operaciones con archivos. El nombre del archivo a analizar los recibimos por la línea de parámetros args[].

Crearemos un objeto "fichero" de tipo FileInputStream, que posteriormente instanciaremos utilizando el constructor:

FileInputStream(String archivo ) throws FileNotFoundExcption

Ya que utilizamos la variable "archivo" que es de tipo String. Controlaremos la existencia del fichero con la excepción adecuada dando un mensaje de error por la salida estándar si no existe.

A continuación leeremos el fichero carácter a carácter hasta que se encuentre el final del archivo, asimismo controlaremos que no se produce ningún tipo de error. Finalmente cerraremos el fichero y visualizaremos los resultados.

La lectura de los archivos carácter a carácter tiene una sola ventaja a mi entender, que es la posibilidad de leer cualquier carácter Unicode y no quedarnos en los 256 caracteres del ASCII.

PAQUETES

A continuación haremos un recorrido por la biblioteca estándar de Java. Veremos los paquetes básicos del lenguaje donde se encuentra la mayor parte de su potencia. No pretendo hacer una presentación completa del API del JDK, ya que solo esto de forma esquemática ocuparía más de 300 páginas, sólo describiremos cada paquete y señalaremos las clases y métodos más importantes. Para una mayor información podemos recurrir a la propia documentación del JDK o algún otro manual que la incorpora. Es necesario indicar que este API crece y evoluciona rápidamente por lo que nos podemos quedar obsoletos en poco tiempo.
Paquete java.lang:
Es el más íntimamente relacionado con el lenguaje. Contiene :
Dentro de las clases señalaremos:
Clase Math:
Esta Pseudoclase define las principales cosntantes matemáticas y las funciones más habituales. La función Log se refiere a los logaritmos neperianos.
public static final Double E=2.718281828459045
public static final Double PI=3.14592653589793
public static Double abs(Double a)
public static float abs(float a)
public static int abs(int a)
public static native Double acos(Double a)
public static native Double asin(Double a)
public static native Double atan(Double a)
idem para las funciones: cos, exp, floor, log, max, min, round, sin, sqrt, tan.
Clase Object:
Esta clase es la antecesora de todas las demás. Define comportamientos predeterminados para los métodos comunes a todos los objetos.
Clase OutOfMemoryError:
Error que señala la falta de memoria. Típico error que en los ejemplos y manuales no se utiliza pero que en la vida real se debe de usar para controlar la falta de memoria en la ejecución de programas.
Clase String, StringBuffer, Thread :
Estas clases ya las hemos repasamos en capítulos anteriores, indicar que pertenecen a este paquete.
Clase System:
Se utiliza para manipular algunas funciones relacionadas con el sistema operativo. En particular define los principales flujos de entrada, salida y error.
Paquete java.util:
Este paquete define clases de utilidad. Se agrupan en las familias siguientes:
Clase Calendar:
Clase abstracta que permite la conversión entre un objeto Date y valores enteros de tipo Día, Mes, Año, etc. Permite también efectuar operaciones aritméticas sobre las fechas. Esta clase define constantes que representan los meses, los días, y ciertos métodos de cálculo.
public static final int ERA=0
public static final int YEAR=1
public static final int MONTH=2
public static final int SUNDAY=1
public static final int MONDAY=2
.......
public static final int JUNARY=0
public static final int FEBRUARY=1
.......
public static sysnchronized Calendar getinstance()
public static sysnchronized Calendar getinstance(TimeZone, zone)
......
public int getFirstDaysinFirstWeek()
public final Date getTime()
protected void setTimeinMillis(long)
public final void set(int year, int month, int date)
Clase Hashtable:
define una estructura de datos tipo matriz con claves hashing. Asocia un valor con una clave permitiendo recuperar el valor muy rápidamente. Se utiliza cuando se quiera almacenar valores con un índice no numérico.
Clase Properties:
extensión de la clase anterior que permite leer y escribir en un flujo una lista de pares clave-valor:
// constructores
public Properties()
public Properties(Properties defecto)
// métodos
public String getProperty(String key)
public void list(PrintSttream out)
public Enumeration propertyNames()
Clase Random:
devuelve números pseudoaleatorios.
Clase Timezone:
Representa un uso horario, se utiliza con Calendar y devuelve el uso dado por el sistema.
Clase Vector:
Define una estructura de datos tipo vector(matriz dinámica).
Paquete Java.io:
Contiene todas las clases relativas a las entradas y salidas. Contine:
Muchas de las clases aquí mencionadas las hemos visto en los últimos capítulos de este articulo:
Clase File:
define un conjunto de atributos estáticos dependientes del sistema.
Clase InputStream:
clase abtracta que es el prototipo de todos los objetos que manipulan un flujo de entrada.
Clase FileInputStream:
esta clase es un InputStream asociada a un archivo.
Clase FileNotFoundException:
esta excepcion se activa automáticamente cuando se intenta crear un objeto que referencia a un archivo que no existe.
Clase OutputStream:
clase abstracta que es el prototipo de todos los objetos que manipulan un flujo de salida.
Clase FileOutputStream:
flujo de salida asociado a un archivo.
Clase PipedInputStream:
esta clase permite conectar un flujo de entrada a un flujo de salida.
Clase RandomAccesFile:
propone un interfaz de lectura y escritura a un archivo.
Paquete Java.net:
Este paquete proporciona los mecanismos de trabajo en red. Java admite los protocolos TCP y UDP. Las clases que se utilizan mas frecuentemente son:
Clase ContenHandler:
clase abstracta que es el prototipo de clases especializadas en la interpretación del contenido de un flujo, un caso tipo es el del protocolo HTTP.
Clase HttpUrlConnection:
esta clase está especializada en la gestión de protocolos HTTP. Es utilizada automáticamente por un objeto URL cuando se habré una conexión.
Clase InetAddress:
encapsula las diferentes formas de vincular direcciones Internet a un nombre de anfitrión, por lo tanto encpsula tanto los direcciones IP numéricas como el nombre de domino de esas direcciones.
public String getHostName()
public byte[] getAddress()
Clase ServerSocket:
posee la capacidad de escucha de un puerto TCP. Se trata de un elemento fundamental en el diseño de un servidor.
//constructores
public ServerSocket(int puerto)
public ServerSocket(in puerto, int ba)
//métodos
public Socket accept()
public void close()
public InetAdress getInetAdress()
Clase URL:
permite definir un modo de representación uniforme para el nombrado de objetos.
Paquete Java.applet:
Es paquete aporta las funcionalidades para crear y gestionar los applets.
Clase Applet:
para crear su propia applet tendrá que derivar esta clase y especificar los métodos siguientes:
getAppletInfo:
devuelve información sobre el applet.
getParameterInfo:
idem pero de los parámetros del applet.
Init:
llamada al inicio del applet.
Start:
llamada para lanzar el applet.
Stop:
llamada para parar al applet.
Destroy:
llamada para liberar los recursos ocupados por el applet.
No se preocupe, los dos siguientes artículos los dedicaremos por completo a este paquete.
Paquete Java.awt:
Este paquete engloba toda la interfaz gráfica. Es el que más a crecido y más rápidamente evoluciona y cambia. Tiene innumerables clases y métodos, intentaremos ver los más representativos:
Clase BorderLayout:
Es el gestor de organización con limites.
Clase Button:
crea un botón de control.
Clase Canvas:
crea una ventana sin una semántica asociada.
Clase CardLayout:
gestor de organización con tarjetas.
Clase Chekbox:
cuadro de comprobación.
Clase Choise:
crea una lista emergente.
Clase Color:
gestiona los colores.
Clase Dialog:
crea una ventana de diálogo.
Clase Event:
encapsula eventos.
Clase FileDialog:
crea ventanas para seleccionar archivos.
Clase FlowLayout:
gestor de organización lineal.
Clase Font:
encapsula un tipo de letra.
Clase Frame:
crea una ventana estándar.
Clase Graphics:
encapsula el entorno gráfico.
Clase Image:
encapsula imágenes gráficas.
Clase Label:
crea una etiqueta.
Clase List:
crea una lista donde el usuario puede elegir un elemento.
Clase Menu:
crea un menú desplegable.
Clase MenuBar:
crea una barra de menú.
Clase MenuItem:
crea un elemento de un menú.
Clase Polygony:
encapsula un polígono.
Clase Rectangle:
ídem rectángulo.
Clase Scrollbar:
crea una barra de desplazamiento.
Clase Textarea:
crea un control para un área de texto.
Clase Window:
crea una ventana sin marco, si menú, ni título.
La lista de este paquete, como de los anteriores y de otros que aun faltan, sería interminable y no sería nada didáctico aunque hay que entender que toda la potencia del lenguaje Java está encerrada en todas estas clases, y de aquí deriva también su complejidad; ya que conocer todo los paquetes y sus clases es una tarea bastante larga y compleja, y mucho más aun dominarla.

Habrá observado que en estos capítulos y en estas paginas HTML no se ha encontrado nada en movimiento, es decir ningún Applet que distrajese nuestra vista. No soy muy partidario de ello pero como aperitivo a los capítulos siguientes que estarán dedicados a los paquetes gráficos y los applets incluiré aquí éste que me pareció muy interesante y está basado en el juego incluido en las librerías gráficas de Linux (y UNIX) y la idea viene del manual: Tutorial de Java. Manual en castellano en formato HTML obtenido de la dirección www.fie.us.es/info/internet/JAVA. Agustín Froufe.

He cambiado algunas cosas en el fuente para adaptarlo a mi forma de ser (Los Ojos más redondos y con un poco de estrabismo). El fuente va incluido con el artículo. (para activarlo hay que pasar el puntero del ratón por la imagen).
 



 

Conclusiones:

Con este artículo concluyo la introducción al lenguaje de programación Java para poder con posterioridad dedicarnos ha estudiar aspectos que me parecen más interesantes del lenguaje como su capacidad gráfica (en cuanto a Applet), acceso a bases de datos SQL, trabajos en red, Bean, etc. En le artículo siguiente estudiaremos las Applets, y con ellas detallaremos el paquete gráfico de Java AWT, además veremos algunas interesantes Applets que incluso están muy extendidas en innumerables páginas web de la red.

Referencias:

Texto original en Castellano