1. OutputStreamclase

Recientemente exploramos flujos de entrada. Es hora de hablar de flujos de salida.

La OutputStreamclase es la clase principal para todas las clases que admiten la salida de bytes. Esta es una clase abstracta que no hace nada por sí misma, pero tiene clases descendientes para cada ocasión.

Suena extremadamente complicado. En pocas palabras, esta clase opera con bytes y no, por ejemplo, con caracteres u otros tipos de datos. Y el hecho de que sea abstracto hace que normalmente no lo usemos, sino una de sus clases descendientes. Por ejemplo, FileOutputStreamy similares.

Pero volvamos a la OutputStreamclase. Esta clase tiene métodos que todas sus clases descendientes deben implementar. Aquí están los principales:

Métodos Descripción
void write(int b)
Escribe un byte (no un int) en la secuencia.
void write(byte[] buffer)
Escribe una matriz de bytes en la secuencia
void write(byte[] buffer, off, len)
Escribe parte de una matriz de bytes en la secuencia
void flush()
Escribe todos los datos almacenados en el búfer en la secuencia
void close()
Cierra la corriente

Cuando crea un objeto de una clase que hereda InputStream, generalmente especifica un objeto de origen del que InputStreamlee los datos. Cuando crea un objeto de una clase que hereda OutputStream, también suele especificar el objeto de destino o la secuencia en la que se escribirán los datos.

Repasemos brevemente todos los métodos de la OutputStreamclase:

write(int b)método

Este método escribe un byte (no un int) en el flujo de salida. El valor pasado se convierte en un byte y los primeros tres bytes de int se descartan.

write(byte[] buffer)método

Escribe la matriz dada de bytes en el flujo de salida. Eso es todo.

write(byte[] buffer, int offset, int length)método

Escribe una parte de la matriz de bytes pasada en el flujo de salida. La variable de compensación indica el índice del primer elemento de la matriz y lengthes la longitud del subconjunto que se escribirá.

flush()método

El flush()método se usa para forzar que cualquier dato potencialmente almacenado en el búfer en el flujo actual se escriba en el flujo de destino. Esto es relevante cuando se usa almacenamiento en búfer y/o múltiples objetos de flujo dispuestos en una cadena.

close()método

Escribe cualquier dato no escrito en el objeto de destino. No es necesario llamar al close()método si usa un try-with-resourcesbloque.

Ejemplo de copia de un archivo

Código Nota
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

try(FileInputStream input = new FileInputStream(src);
FileOutputStream output = new FileOutputStream(dest))
{
   byte[] buffer = new byte[65536]; // 64Kb
   while (input.available() > 0)
   {
      int real = input.read(buffer);
      output.write(buffer, 0, real);
   }
}



InputStreampara leer de un archivo
OutputStreampara escribir en un archivo

Búfer en el que leeremos los datos
Siempre que haya datos en el flujo

Leer datos en el búfer
Escribir los datos del búfer en el segundo flujo

2. Writerclase

La Writerclase es exactamente igual que la OutputStreamclase, pero solo una diferencia una vez más: funciona con caracteres ( char) en lugar de bytes.

Esta es una clase abstracta: no puede crear objetos de la Writerclase. Su objetivo principal es ser una clase principal común para cientos de clases descendientes y brindarles métodos comunes para trabajar con flujos de caracteres.

Métodos de la Writerclase (y todas sus clases descendientes):

Métodos Descripción
void write(int b)
Escribe un carácter (no un int) en la secuencia.
void write(char[] buffer)
Escribe una matriz de caracteres en la secuencia.
void write(char[] buffer, off, len)
Escribe parte de una matriz de caracteres en la secuencia
void write(String str)
Escribe una cadena en la secuencia.
void write(String str, off, len)
Escribe parte de una cadena en la secuencia
void flush()
Escribe todos los datos almacenados en el búfer en la secuencia
void close()
Cierra la corriente

Los métodos son muy similares a los métodos de la OutputStreamclase, pero funcionan con caracteres en lugar de bytes.

Descripción de los métodos:

write(int b)método

Este método escribe un solo carácter ( char—no un int) en el flujo de salida. El valor pasado se convierte en a chary los dos primeros bytes se descartan.

write(char[] buffer)método

Escribe la matriz dada de caracteres en el flujo de salida.

write(char[] buffer, int offset, int length)método

Escribe una parte de la matriz de caracteres pasada en el flujo de salida. La offsetvariable indica el índice del primer elemento de la matriz y lengthes la longitud del subconjunto que se escribirá.

write(String str)método

Escribe la cadena dada en el flujo de salida.

write(String str, int offset, int length)método

Escribe una parte de la cadena dada en el flujo de salida: la cadena se convierte en una matriz de caracteres. La offsetvariable indica el índice del primer elemento de la matriz y lengthes la longitud del subconjunto que se escribirá.

flush()método

El flush()método se usa para forzar que cualquier dato potencialmente almacenado en el búfer en el flujo actual se escriba en el flujo de destino. Esto es relevante cuando se usa almacenamiento en búfer y/o múltiples objetos de flujo dispuestos en una cadena.

close()método

Escribe cualquier dato no escrito en el objeto de destino. No es necesario llamar al close()método si usa un try-with-resourcesbloque.

Ejemplo de un programa que copia un archivo de texto:

Código Nota
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

try(FileReader reader = new FileReader(src);
FileWriter writer = new FileWriter(dest))
{
   char[] buffer = new char[65536]; // 128Kb
   while (reader.ready())
   {
      int real = reader.read(buffer);
      writer.write(buffer, 0, real);
   }
}



Readerpara leer de un archivo
Writerpara escribir en un archivo

Búfer en el que leeremos los datos
Siempre que haya datos en el flujo

Leer datos en un búfer
Escribir los datos del búfer en el segundo flujo

StringWriterclase

Hay otra clase interesante que hereda la Writerclase: se llama StringWriter. Contiene una cadena mutable: un StringBufferobjeto. Y cada vez que "escribe" algo en el StringWriterobjeto, el texto simplemente se agrega a su búfer interno.

Ejemplo:

Código Nota
StringWriter writer = new StringWriter();
writer.write("Hello");
writer.write(String.valueOf(123));

String result = writer.toString();
StringWriterSe crea un flujo de caracteres de destino ( )
Se escribe una cadena en el búfer dentro de StringWriter
Se escribe una cadena en el búfer dentro de StringWriter

Conversión del contenido de un objeto en una cadena

En este caso, la StringWriterclase es esencialmente un contenedor sobre la StringBufferclase, pero la StringWriterclase es un descendiente de la Writerclase de flujo y se puede usar en cadenas de objetos de flujo. Esta es una propiedad bastante útil en la práctica.



3. PrintStreamclase

Las clases de flujo de salida también se pueden poner en una cadena con flujos intermediarios que escriben datos en el flujo de destino que se les pasa. La vista general de la interacción de estos flujos se ve así:

clase PrintStream

El más interesante y versátil de todos los flujos de salida intermedios es PrintStream. Tiene decenas de métodos y hasta 12 constructores.

La PrintStreamclase hereda la FilterOutputStreamclase, que hereda OutputStream. Eso significa que la PrintStreamclase tiene todos los métodos de las clases principales además de sus propios métodos . Aquí están los más interesantes:

Métodos Descripción
void print(obj)
Convierte el objeto pasado en una cadena y lo envía a la secuencia de destino.
void println(obj)
Convierte el objeto pasado en una cadena y lo envía a la secuencia de destino. Agrega un salto de línea al final
void println()
Envía un carácter de salto de línea al flujo de destino
PrintStream format(String format, args...)
Construye y genera una cadena basada en la cadena de formato y los argumentos pasados; similar al String.format()método

¿Y dónde están estas decenas de métodos, preguntas?

Bueno, tiene muchas variantes de los métodos print()y println()con diferentes parámetros. Se pueden resumir en esta tabla.

No profundizaremos en estos métodos, porque ya los conoces bien. ¿Puedes adivinar a lo que me refiero?

¿ Recuerdas System.out.println()? Pero se puede escribir en dos líneas:

Código Salida de consola
PrintStream stream = System.out;
stream.println("Hello!");
Hello!

Nuestro comando favorito es una llamada al método en la variable estática de la clase. Y el tipo de esta variable es .System.out.println()println()outSystemPrintStream

¡En muchos niveles de CodeGym, y en casi todas las tareas, has estado llamando a métodos de la PrintStreamclase sin siquiera saberlo!

Uso práctico

Java tiene esta interesante clase llamada ByteArrayOutputStream, que es una matriz de bytes que crece dinámicamente y que hereda OutputStream.

Un ByteArrayOutputStreamobjeto y PrintStreamun objeto se pueden encadenar así:

Código Descripción
ByteArrayOutputStream baos = new ByteArrayOutputStream();

try(PrintStream stream = new PrintStream(baos))
{
   stream.println("Hello");
   stream.println(123);
}

String result = baos.toString();

System.out.println(result);
Cree un búfer de escritura en la memoria.

Envuelva el búfer en un PrintStreamobjeto

. Escriba datos en la consola.



¡Convierta la matriz en una cadena!

Salida de la consola:
Hello!
123