Free Web Hosting Provider - Web Hosting - E-commerce - High Speed Internet - Free Web Page
Search the Web

Java.

Autor:

Pedro Asencio; esto no es la gran cosa en lo que respecta a Java pero te servira como introducción. Alguna otra duda escribeme a: pasencio16@yahoo.com


PROGRAMACION EN JAVA

Cuando se programa en Java, se coloca todo el código en métodos, de la misma forma que se escriben funciones en lenguajes como C.

Comentarios

En Java hay tres tipos de comentarios:

    // comentarios para una sola línea

    /* comentarios de una o
        más líneas
    */

    /** comentario de documentación, de una o más líneas
    */

Los dos primeros tipos de comentarios son los que todo programador conoce y se utilizan del mismo modo. Los comentarios de documentación, colocados inmediatamente antes de una declaración (de variable o función), indican que ese comentario ha de ser colocado en la documentación que se genera automáticamente cuando se utiliza la herramienta de Java, javadoc. Dichos comentarios sirven como descripción del elemento declarado permitiendo generar una documentación de nuestras clases escrita al mismo tiempo que se genera el código.

En este tipo de comentario para documentación, se permite la introducción de algunos tokens o palabras clave, que harán que la información que les sigue aparezca de forma diferente al resto en la documentación.

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 símbolo de dólar ($). Los siguientes caracteres pueden ser letras o dígitos. Se distinguen las mayúsculas de las minúsculas y no hay longitud máxima.

Serían identificadores válidos:

    identificador
    nombre_usuario
    Nombre_Usuario
    _variable_del_sistema
    $transaccion

y su uso sería, por ejemplo:

    int contador_principal;
    char _lista_de_ficheros;
    float $cantidad_en_Ptas;

Palabras clave
Las siguientes son las palabras clave que están definidas en Java y que no se pueden utilizar como indentificadores:

    abstract    continue    for         new         switch
    boolean     default     goto        null        synchronized
    break       do          if          package     this
    byte        double      implements  private     threadsafe
    byvalue     else        import      protected   throw
    case        extends     instanceof  public      transient
    catch       false       int         return      true
    char        final       interface   short       try
    class       finally     long        static      void
    const       float       native      super       while

Palabras Reservadas
Además, el lenguaje se reserva unas cuantas palabras más, pero que hasta ahora no tienen un cometido específico. Son:

    cast        future      generic     inner
    operator    outer       rest        var

Literales

Un valor constante en Java se crea utilizando una representación 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 código fuente de Java. Cada uno de estos literales tiene un tipo correspondiente asociado con él.

Enteros:

    byte  		8 bits  		complemento a dos
    short  		16 bits  		complemento a  dos
    int  		32 bits  		complemento a dos
    long  		64 bits  		complemento a dos
    Por ejemplo:	21      077     0xDC00

Reales en coma flotante:

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

Booleanos:

    true
    false

Caracteres:

    Por ejemplo:	a       \t      \u????    [????] es un número unicode

Cadenas:

    Por ejemplo:	"Esto es una cadena literal"

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 límites de los arrays se comprueban en tiempo de ejecución para evitar desbordamientos y la corrupción de memoria.

En Java un array es realmente un objeto, porque tiene redefinido el operador []. Tiene una función miembro: length. Se puede utilizar este método 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 métodos básicos. Crear un array vacío:

    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( "Juan" );
    nombres[1] = new String( "Pepe" );
    nombres[2] = new String( "Pedro" );
    nombres[3] = new String( "Maria" );

No se pueden crear arrays estáticos en tiempo de compilación:

    int lista[50];  // generará un error en tiempo de compilación

Tampoco se puede rellenar un array sin declarar el tamaño con el operador new:

    int lista[];
    for( int i=0; i < 9; i++ )
        lista[i] = i;

Es decir, todos los arrays en Java son estáticos. Para convertir un array en el equivalente a un array dinámico en C/C++, se usa la clase vector, que permite operaciones de inserción, borrado, etc. en el array.

Operadores

Los operadores de Java son muy parecidos en estilo y funcionamiento a los de C. En la siguiente tabla aparecen los operadores que se utilizan en Java, por orden de precedencia:

    .       []      ()
    ++      --
    !       ~       instanceof
    *       /       %
    +       -
    <<      >>      >>>
    <       >       <=       >=       ==       !=
    &       ^       |
    &&      ||
    ?  :
    =       op=     (*=     /=      %=      +=      -=      etc.)    ,

Los operadores numéricos se comportan como esperamos:

    int + int = int

Los operadores relacionales devuelven un valor booleano.

Para las cadenas, se pueden utilizar los operadores relacionales para comparaciones además de + y += para la concatenación:

    String nombre = "nombre" + "Apellido";

El operador = siempre hace copias de objetos, marcando los antiguos para borrarlos, y ya se encargará el garbage collector de devolver al sistema la memoria ocupada por el objeto eliminado.

Separadores

Sólo hay un par de secuencias con otros caracteres que pueden aparecer en el código Java; son los separadores simples, que van a definir la forma y función del código. Los separadores admitidos en Java son:

() - paréntesis. Para contener listas de parámetros en la definición y llamada a métodos. También 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 automáticamente. También se utiliza para definir un bloque de código, para clases, métodos y ámbitos locales.

[] - corchetes. Para declarar tipos matriz. También se utiliza cuando se referencian valores de matriz.

; - punto y coma. Separa sentencias.

, - coma. Separa identificadores consecutivos en una declaración de variables. También se utiliza para encadenar sentencias dentro de una sentencia for.

. - punto. Para separar nombres de paquete de subpaquetes y clases. También se utiliza para separar una variable o método de una variable de referencia.

Soporte de operaciones de entrada y salida: java.io

Como mencionamos anteriormente, Java dispone de una serie de librerías (paquetes) estándar que ofrecen clases para las necesidades más comunes, como se puede ver en la Tabla A. En este artículo abordaremos el soporte de Java para operaciones de entrada y salida, así como para la gestión del sistema de archivos, que se encuentra en el paquete java.io. Cuando pensamos en las operaciones de entrada y salida, se suele pensar en dos tipos de operaciones: operaciones de lectura/escritura sobre archivos, y operaciones de introducción de datos mediante el teclado. Si bien esto resulta muy común, a la hora de la verdad, una aplicación puede obtener datos de muchas otras formas: a través de una conexión vía Internet con un servidor remoto, a través de una conexión DDE con otro programa en la misma máquina, o incluso a través del portapapeles de Windows. Se puede ver que, a pesar de la distinta procedencia de la información en todos estos casos, su manejo será bastante similar: solicitamos al sistema que nos conecte a la fuente o destino de la información (abrimos archivo, nos conectamos al servidor de red, etc.), obtenemos la información, que será una serie de datos secuenciales, y nos desconectamos de la fuente de datos, para liberar recursos del sistema. Java utiliza el concepto de flujo (stream) para trabajar con información manejada secuencialmente.

java.applet  Librería para creación y manejo de applets
java.awt  Librería de interface gráfico
java.io  Librería para operaciones de Entrada/Salida
java.lang Librería con clases básicas
java.net Librería de soporte para programación en red
java.util  Clases de utilidad, como pilas, etc.


Tabla A: Las librerías estándar de Java.
No siempre se puede o es conveniente trabajar secuencialmente: hay ocasiones en que deseamos tener acceso aleatorio, en lugar de leerla en serie hasta que llegamos a la posición donde está la información que deseamos. Java también proporciona soporte para acceso aleatorio a archivos, a través de la clase RandomAccessFile. El acceso al sistema de archivos del sistema también es fundamental: es necesario poder renombrar archivos, obtener la lista de archivos de un directorio, saber si un archivo es de escritura o lectura, etc. Java proporciona la clase File para manejar el sistema de archivos, independientemente de las clases basadas en flujos y de acceso aleatorio que utiliza para leer/escribir en ellos. La Tabla B muestra las clases de entrada y salida que se pueden encontrar en java.io.

InputStream 
ByteArrayInputStream 
  FileInputStream 
  PipedInputStream 
  SequenceInputStream 
  StringBufferInputStream 
  FilterInputStream 
    BufferedInputStream 
    DataInputStream 
    LineNumberInputStream 
    PushbackInputStream 
OutputStream 
  ByteArrayOutputStream 
  FileOutputStream 
  PipedOutputStream 
  FilterOutputStream 
    BufferedOutputStream 
    DataOutputStream 
    PrintStream 
File 
FileDescriptor 
RandomAccessFile 
StreamTokenizer


Tabla B: Clases en el paquete java.io
Por último, Java proporciona una clase de excepción para cada tipo de error común en las operaciones de entrada y salida: cada una de estas clases deriva de la clase base IOException. Abordaremos cada una de estas partes del paquete java.io por separado.

Flujos de datos


El concepto de flujo es muy potente, dado que proporciona un modo de tratar las operaciones de entrada/salida de forma similar para distintas fuentes de datos y canales de comunicación. Podemos definir un flujo como una secuencia de bytes que viajan desde una fuente a un destino a través de un camino, de modo secuencial. Java proporciona un conjunto de clases para leer información desde un flujo, y otro para escribir en él. Las dos clases fundamentales son InputStream y OutputStream, para lectura y escritura respectivamente, que proporcionan métodos para realizar las operaciones básicas: en función de las distintas fuentes o modos de manejar la información se tendrán distintas clases derivadas de éstas, siempre respetando el protocolo básico dictado por ellas. El esquema básico de trabajo con los flujos es siempre el mismo: se abren, lo que se consigue con las operaciones new InputStreamFile(...), etc., se realizan las operaciones deseadas de escritura y lectura con read, write, etc., y luego se cierran, con close(). El Listado A muestra un programa que lee un archivo y lo copia en otro, utilizando flujos. Nótese que utilizamos las clases FileInputStream y FileOutputStream, derivadas de InputStream y OutputStream y con los mismo métodos.

import java.io.*; 

public class entrada_salida1 { 
  static void main( String args[] ) 
     throws IOException 
  { 
    int caracter; 
    int fin_archivo = -1; 

    // Creamos y abrimos los flujos 
    InputStream entrada = 
       new FileInputStream( "c:\\autoexec.bat" ); 
    OutputStream salida = 
       new FileOutputStream( "c:\\copia.aux" ); 

    // Realizamos operaciones de entrada y salida 
    caracter = entrada.read(); 
    while( caracter != fin_archivo ) { 
       salida.write( caracter ); 
       caracter = entrada.read(); 
     } 

     // Cerramos los flujos 
     entrada.close(); 
     salida.close(); 
  } 
};


Listado A: Programa que copia un archivo en otro (entrada_salida1.java)
Vale la pena destacar un par de puntos en el Listado A:  en primer lugar, en cualquier punto del programa se puede producir un error de entrada/salida, del tipo IOException, motivo por el que lo hemos indicado en la primera línea del método main, mediante el código throws IOException: no hay peligro de olvidar esto, porque Java se negará a compilar, como ya vimos en el artículo de Marzo sobre excepciones. Otro punto importante es que hemos creado objetos de las clases FileInputStream y FileOutputStream, pero los hemos asignado a variables de las clases InputStream y OutputStream: esto funciona debido al polimorfismo. Dado que las clases FileXXXStream son derivadas de las clases XXXStream, se llamará al método adecuado de FileXXXStream, clases a las que realmente pertenecen los objeto asignados a la variable. Además de funcionar, este modo de codificar es conveniente: así, si deseamos copiar el archivo a pantalla, bastará con asignar a salida un flujo que sea capaz de escribir en pantalla, en lugar de en un archivo, como puede ser System.out, que ya hemos utilizado en otros artículos, y que es un objeto de la clase PrintStream, derivada de OutputStream. Bastará con escribir     OutputStream salida = System.out; en lugar de      OutputStream salida = new FileOutputStream...; sin modificar ningún otro fragmento de código (se puede encontrar el código fuente en el archivo entrada_salida2.java incluido en el disco). Alternativamente, en lugar de enviar la información de salida a la pantalla podríamos escribir en memoria compartida entre varios procesos, a una conexión remota, o a casi cualquier cosa capaz de almacenar información, siempre que tengamos una clase XXXStream adecuada. En el Listado B se pueden ver los métodos de OutputStream y en la Listado C los de InputStream. Estudiaremos estas dos clases básicas en detalle, y luego las clases derivadas, explicando en qué se diferencian y qué añaden. Todo lo que sepamos de las clases base se cumplirá también para las derivadas: al fin y al cabo, en Java cuando derivamos de una clase estamos comprometiéndonos a que la clase derivada se comporte como esta, posiblemente añadiendo nuevas capacidades.

public  abstract  class 
  java.io.OutputStream 
    extends  java.lang.Object 

  // Constructores 
  public OutputStream(); 

  // Métodos 
  public void write(byte  b[]); 
  public void write(byte  b[], int  comienzo, int  l); 
  public abstract void write(int  b); 
  public void close(); 
  public void flush(); 
}


Listado B: La clase base para flujos de salida/escritura, OutputStream
La clase OutputStream proporciona varios métodos para escritura, todos llamados write (en el número de Febrero comentamos que Java soportaba la sobrecarga, es decir, tener varios métodos con el mismo nombre). La primera versión del listado proporciona la posibilidad de escribir varios bytes a la vez (un array), así como el segundo, que permite indicar qué parte del array es la que deseamos tratar: comienzo es el lugar desde el que deseamos comenzar a copiar, y l el número de bytes a partir de dicha posición. Por fin, la última versión de write, que es la que hemos utilizado en nuestro programa, simplemente escribe un entero. Otro método importante es close, que cierra el flujo, y que es importante no olvidar si deseamos que no se pierda información inadvertidamente. En cuanto a flush, es un método destinado a asegurarse de que realmente se guarda la información: muy a menudo ésta no va a parar directamente al lugar de destino, sino que se guarda en memoria intermedia, de modo que se escriba todo un bloque de información de una vez para evitar continuos accesos al lugar de destino, haciendo así más rápido el proceso. Esto, sin embargo, puede hacer que perdamos información si se cae el sistema, motivo por el que existe flush, que fuerza la escritura rea. Como se puede ver, OutputStream es una clase abstracta, destinada a ofrecer un protocolo estándar a todos los flujos de escritura, y nada más: cada clase derivada se encarga de implementar cada método de la manera más adecuada al destino de la información y al canal utilizado para transmitirla.

public  abstract  class 
  java.io.InputStream 
     extends  java.lang.Object 

  // Constructores 
  public InputStream(); 

  // Métodos 
  public abstract int read(); 
  public int read(byte  b[]); 
  public int read(byte  b[], int  comienzo, int  l); 
  public void close(); 

  public long skip(long  n); 
  public int available(); 

  public void mark(int  limiteLectura); 
  public boolean markSupported(); 
  public void reset(); 
}


Listado C: La clase base de entrada/lectura, InputStream
La clase InputStream es la contraparte de OutputStream utilizada para lectura. Para leer del flujo contamos con tres versiones del método read: la primera versión en el Listado C lee un entero, y la segunda y la tercera un array de bytes, devolviendo el número de bytes leídos. Todos estos métodos devuelven -1 para indicar que se ha encontrado el final del flujo, y no hay más datos a recuperar. Al igual que con los flujos de salida, es necesario cerrar un InputStream una vez que hemos terminado de utilizarlo, mediante close. Aparte de estos métodos, tenemos skip, que avanza n bytes en el flujo de entrada, saltándoselos, y available, que determina el número de bytes que se pueden leer. Nótese que es posible que available devuelva 0 siempre para cierto tipo de flujos en algunos sistemas, por lo que se ha de tener precaución a la hora de utilizarlo. Otra posibilidad interesante en un flujo es la capacidad de recordar la posición donde hemos estado en un momento dado, para luego volver a ella: esto se implementa mediante mark, que memoriza la posición actual, método al que se le pasa como parámetro el número de bytes que se pueden leer sin que el marcador quede invalidado. El método reset nos devuelve a la última posición marcada. Es posible que determinados tipos de flujos no soporten la posibilidad de marcar cierta posición y volver a ella: por ejemplo, se podría plantear si tiene sentido volver a una posición anterior en un flujo de entrada asociado a la entrada por teclado. Para averiguar si cierto flujo soporta o no el uso de marcadores se puede utilizar markSupported. Nótese que el hecho de que no se pueda garantizar la validez del uso de marcadores no es un problema de la implementación en Java, sino un reflejo de la diversidad de los dispositivos y de los que se puede obtener información de entrada en el mundo real, cada uno con sus distintas capacidades.

Escritura/lectura secuencial de archivos

Como hemos visto en el Listado A, Java soporta la escritura/lectura de archivos mediante las clases FileOutputStream y FileInputStream, derivadas de OutputStream e InputStream, respectivamente. Absolutamente todos los métodos que hemos visto para las clases base de manejo de flujos funcionan tal y como se vio, por lo que solo expondremos las novedades que presentan estas clases, o las pequeñas variaciones que puedan tener algunos métodos.  El Listado D muestra los nuevos métodos y constructores de FileOutputStream. Evidentemente, para construir un flujo que funcione sobre un archivo, habrá que especificar de algún modo el archivo: el mejor lugar para ello es el constructor del flujo. La clase proporciona tres constructores para especificar el archivo: el primero en el listado permite indicarlo simplemente especificando el nombre. El segundo constructor recibe como parámetro un objeto de la clase File, utilizado por Java para representar los archivos y manejarlos (renombrarlos, eliminarlos, etc.), y cuyo estudio abordaremos más adelante. El tercer constructor toma como parámetro un FileDescriptor, que es otra clase utilizada para representar un archivo: la estudiaremos junto con File. Por último, tenemos un único método nuevo, getFD, que simplemente devuelve el FileDescriptor asociado al archivo sobre el que trabaja el flujo.

public  class  java.io.FileOutputStream
  extends  java.io.OutputStream
{
  // Constructores
  public FileOutputStream(String  nombreArchivo);
  public FileOutputStream(File  archivo);
  public FileOutputStream(FileDescriptor  fd);

  // Métodos
  public final FileDescriptor getFD();

  // ...
}


Listado D: Métodos que FileOutputStream añade con respecto a OutputStream.
En cuanto a FileInputStream, añade exactamente los mismos constructores y métodos con respecto a InputStream que FileOutputStream con respecto a OutputStream.


Flujos en memoria

Es posible que deseemos manejar a veces un buffer en memoria (array) o una cadena de texto como un flujo. Aunque en principio puede parecer muy extraño querer manejar una cadena de este modo, esto nos permite escribir código para tratar del mismo modo cadenas, bloques de memoria o archivos. Si, por ejemplo, nos construimos un pequeño intérprete que analice código escrito en un archivo, ¿por qué no escribirlo basándose en las clases de flujo, de modo que se pueda también interpretar información escrita directamente por el usuario, y que el programa obtiene de él como una cadena?. De este modo, ahorraríamos el trabajo de escribir la cadena introducida por el usuario en un archivo, y obtendríamos una mejora de velocidad, al no tener que pasar el código a disco. Las clases que proporciona Java para esto son ByteArrayInputStream, ByteArrayOutputStream y StringBufferInputStream. ByteArrayInputStream, en el Listado E, no añade ningún nuevo método a InputStream, clase de la que, como FileInputStream, deriva. Eso sí, el método available está garantizado que devuelve el número de bytes en memoria, y además existe la particularidad de que reset nos lleva al comienzo del buffer, en lugar de a un marcador guardado con mark. Como un ByteArrayInputStream se construye sobre un array de bytes, necesitaremos constructores que tengan en cuenta este hecho: el primero del listado permite especificar el array del que el flujo obtiene los datos, mientras que el segundo especifica el array, pero solo una parte del mismo, indicando esto a través de los parámetros c, la posición de comienzo, y l, el número de bytes a tener en cuenta a partir de la posición de comienzo. La clase StringBufferStream es idéntica a ésta, salvo que en lugar de obtener la información de un array de bytes, la obtenemos de una cadena ( un StringBuffer).

public  class 
  java.io.ByteArrayInputStream
     extends  java.io.InputStream
{
  // Constructores
  public ByteArrayInputStream(byte  buf[]);
  public ByteArrayInputStream(byte  buf[], 
                              int c, int l );

  // ...
}


Listado E: Métodos que ByteArrayInputStream añade a su clase base, InputStream
En cuanto a ByteArrayOutputStream, cuyos nuevos métodos se pueden encontrar en el Listado F, es un buffer dinámico, que crece conforme le vamos añadiendo datos. Se le puede especificar un tamaño base, como se puede ver en el primer constructor del listado, o bien dejar que tenga un tamaño inicial por defecto, utilizando el segundo constructor. La clase ByteArrayOutputStream ofrece varios métodos nuevos con respecto a OutputStream. Es posible saber el número de bytes que se han escrito mediante size. Además, es posible obtener un array con los datos del flujo, mediante toByteArray, o cadenas de texto, mediante las dos versiones de toString en el Listado F. Esto último puede ser útil, dado que resulta muy común que lo que manejemos en memoria no sea más que una cadena de texto. Como una comodidad adicional, existe la posibilidad de pasar toda la información almacenada en la memoria por el flujo a otro flujo de salida, como un archivo, etc., mediante el método writeTo( flujoSalida).

public  class 
  java.io.ByteArrayOutputStream
    extends  java.io.OutputStream
{
  // Constructores
  public ByteArrayOutputStream(int  tamanyo);
  public ByteArrayOutputStream();

  // Métodos
  public int size();
  public byte[] toByteArray();
  public String toString();
  public String toString(int  hibyte);
  public void writeTo(OutputStream  os);
}


Listado F: Métodos que ByteArrayOutputStream añade a OutputStream.

Comunicación entre procesos/threads mediante flujos

Además de las clases vistas hasta ahora, Java proporciona unos flujos especiales para comunicación entre threads: la ventaja de utilizar este modo de comunicación es que Java se encargará de todas las tareas de sincronización en el acceso a los datos, de modo que los procesos lectores y escritores no choquen. Las clases utilizadas para llevar a cabo esta tarea son PipedOutputStream y PipedInputStream. La idea básica aquí es que tenemos un objeto de cada clase, y los threads lectores usan el de la clase PipedInputStream, y los procesos escritores el de la clase PipedOutputStream, a través de los cuales se accede a una misma información: para ponerlos de acuerdo en que esto es así, hay que conectar el flujo de entrada con el de salida, lo que se hace mediante código como el que sigue:     pipeEntrada.connect( pipeSalida ); o bien     pipeSalida.connect( pipeEntrada ); con lo cuál ambos trabajarán sobre la misma información. Como se puede ver, el método connect existe para ambas clases, y es el único método que añaden a sus clases base, que como de costumbre son InputStream y OuputStream. Además, la operación de poner de acuerdo a ambos flujos también se puede llevar a cabo mediante un constructor que proporcionan y que permite pasar como parámetro el pipe complementario, lo que hace innecesario llamar a connect.

Concatenar flujos de entrada con SequenceInputStream

Java proporciona una clase de utilidad que nos permite manipular varios flujos de lectura como si fuesen uno solo, concatenándolos uno tras otro. La clase es SequenceInputStream, derivada de InputStream, y el Listado G es un pequeño programa que concatena a efectos de lectura los archivos AUTOEXEC.BAT y CONFIG.SYS. El programa es muy similar al del Listado A, solo que en lugar de tomar la entrada de un archivo, toma la entrada de un SequenceInputStream que concatena a efectos de lectura dos archivos. Como con las distintas clases vistas hasta ahora, esta clase define constructores apropiados: en este caso, el constructor utilizado admite como argumentos dos flujos de entrada (InputStream) cualesquiera. Hay otro constructor que permite pasar una lista, en lugar de dos, mediante un argumento del tipo Enumerated.

import java.io.*;

public class entrada_salida3 {
  static void main( String args[] ) 
     throws IOException 
  {
    int caracter;
    int fin_archivo = -1;

    // Creamos y abrimos los flujos
    FileInputStream autoexec = 
        new FileInputStream( "c:\\autoexec.bat" );
    FileInputStream config   = 
        new FileInputStream( "c:\\config.sys" );
    InputStream entrada = 
        new SequenceInputStream( autoexec, config );
    OutputStream salida = 
        new FileOutputStream( "c:\\copia.aux" );

    // Realizamos operaciones de entrada y salida
    caracter = entrada.read();
    while( caracter != fin_archivo ) {
      salida.write( caracter );
      caracter = entrada.read();
    }

    // Cerramos los flujos
    entrada.close();
    salida.close();
  }
};


Listado G: Uso de SequenceInputStream (entrada_salida3.java).

Principal.//Archivos. //Chat.//Miembros.//Programacion.//Musica.//Herramientas Hacker//101 Tips para W1nDOw$.