Guía de OutputStream de Java

1. Información general

En este tutorial, exploraremos detalles sobre la clase de Java OutputStream . O utputStream es una clase abstracta. Esto sirve como superclase para todas las clases que representan un flujo de salida de bytes.

Examinaremos qué significan estas palabras como "salida" y "flujo" con más detalle a medida que avanzamos.

2. Breve introducción a Java IO

OutputStream es parte de la API de IO de Java que define las clases necesarias para realizar operaciones de I / O en Java. Todos estos están empaquetados en el espacio de nombres java.io. Este es uno de los paquetes principales disponibles en Java desde la versión 1.0.

A partir de Java 1.4, también tenemos Java NIO empaquetado en el espacio de nombres java.nio que permite operaciones de entrada y salida sin bloqueo. Nuestra área de enfoque para este artículo, sin embargo, es ObjectStream como parte de Java IO.

Los detalles relacionados con Java IO y Java NIO se pueden encontrar aquí.

2.1. Entrada y salida

Java IO básicamente proporciona un mecanismo para leer datos de una fuente y escribir datos en un destino . La entrada representa la fuente, mientras que la salida representa el destino aquí.

Estos orígenes y destinos pueden ser cualquier cosa, desde archivos, tuberías hasta conexiones de red.

2.2. Corrientes

Java IO proporciona el concepto de flujos que básicamente representa un flujo continuo de datos . Las secuencias pueden admitir muchos tipos diferentes de datos como bytes, caracteres, objetos, etc.

Además, la conexión a una fuente o un destino es lo que representa una secuencia. Por lo tanto, vienen como InputStream o OutputStream respectivamente.

3. Interfaces de OutputStream

OutputStream implementa un montón de interfaces que proporcionan algún carácter distintivo a sus subclases. Repasemos rápidamente.

3.1. Cerrable

La interfaz Closeable proporciona un método llamado close () que maneja el cierre de una fuente o un destino de datos. Cada implementación de OutputStream debe proporcionar una implementación de este método. Aquí pueden realizar acciones para liberar recursos.

3.2. AutoCloseable

La interfaz AutoCloseable también proporciona un método llamado close () con un comportamiento similar al de Closeable . En este caso, sin embargo, el método close () se llama automáticamente al salir de un bloque try-with-resource.

Puede encontrar más detalles sobre try-with-resource aquí.

3.3. Desechable

La interfaz Flushable proporciona un método llamado flush () que maneja la descarga de datos a un destino.

Una implementación particular de OutputStream puede optar por almacenar en búfer los bytes escritos previamente para optimizar, pero una llamada a flush () lo hace escribir en el destino inmediatamente .

4. Métodos en OutputStream

OutputStream tiene varios métodos que cada clase de implementación debe implementar para sus respectivos tipos de datos.

Estos son, aparte de close () y flush () métodos que hereda de que se pueda cerrar y Flushable interfaces.

4.1. escribir (int b)

Podemos usar este método para escribir un byte específico en OutputStream . Dado que el argumento "int" consta de cuatro bytes, como parte del contrato, solo se escribe el primer byte de orden inferior y se ignoran los tres bytes restantes de orden superior:

public static void fileOutputStreamByteSingle(String file, String data) throws IOException { byte[] bytes = data.getBytes(); try (OutputStream out = new FileOutputStream(file)) { out.write(bytes[6]); } }

Si llamamos a este método con datos como "¡Hola mundo!", Lo que obtenemos como resultado es un archivo con el siguiente texto:

W

Este, como podemos ver, es el séptimo carácter de la cadena indexada sexto.

4.2. escribir (byte [] b, int off, int length)

Esta versión sobrecargada del método write () está ahí para escribir una subsecuencia de la matriz de bytes en OutputStream .

Puede escribir el número de bytes de "longitud" de la matriz de bytes según lo especificado por el argumento que comienza en un desplazamiento determinado por "off" en OutputStream:

public static void fileOutputStreamByteSubSequence( String file, String data) throws IOException { byte[] bytes = data.getBytes(); try (OutputStream out = new FileOutputStream(file)) { out.write(bytes, 6, 5); } }

Si ahora llamamos a este método con los mismos datos que antes, obtenemos el siguiente texto en nuestro archivo de salida:

World

Esta es la subcadena de nuestros datos que comienza en el índice cinco y comprende cinco caracteres.

4.3. escribir (byte [] b)

Esta es otra versión sobrecargada del método write () que puede escribir una matriz de bytes completa según lo especificado por el argumento de OutputStream .

Esto tiene el mismo efecto que una llamada a escribir (b, 0, b.lengh) :

public static void fileOutputStreamByteSequence(String file, String data) throws IOException { byte[] bytes = data.getBytes(); try (OutputStream out = new FileOutputStream(file)) { out.write(bytes); } }

Cuando llamamos a este método ahora con los mismos datos, tenemos la cadena completa en nuestro archivo de salida:

Hello World!

5. Direct Subclasses of OutputStream

Now we'll discuss some of the direct known subclasses of OutputStream which individually represent a specific data type of which the OutputStream they define.

They define their own methods apart from implementing those inherited from OutputStream.

We won't go into the details of these subclasses.

5.1. FileOutputStream

As the name suggests, a FileOutputStream is an OutputStream to write data to a File. FileOutputStream, like any other OutputStream, can write a stream of raw bytes.

We have already examined different methods in FileOutputStream as part of the last section.

5.2. ByteArrayOutputStream

ByteArrayOutputStream is an implementation of OutputStream that can write data into a byte array. The buffer keeps growing as ByteArrayOutputStream writes data to it.

We can keep the default initial size of the buffer as 32 bytes or set a specific size using one of the constructors available.

The important thing to note here is that the method close() has practically no effect. The other methods in ByteArrayOutputStream can be safely called even after close() has been called.

5.3. FilterOutputStream

OutputStream primarily writes a byte stream to a destination, but it can as well transform the data before doing so. FilterOutputStream represents superclass of all such classes which perform a specific data transformation. FilterOutputStream is always constructed with an existing OutputStream.

Some of the examples of FilterOutputStream are BufferedOutputStream, CheckedOutputStream, CipherOutputStream, DataOutputStream, DeflaterOutputStream, DigestOutputStream, InflaterOutputStream, PrintStream.

5.4. ObjectOutputStream

ObjectOutputStream can write primitive data types and graphs of Java objects to a destination. We can construct an ObjectOutputStream using an existing OutputStream to write to a specific destination like File.

Please note that it is necessary for objects to implement Serializable for ObjectOutputStream to write them to a destination. You can find more details on Java Serialization here.

5.5. PipedOutputStream

A PipedOutputStream is useful to create a communication pipe. PipedOutputStream can write data which a connected PipedInputStream can read.

PipedOutputStream features a constructor to connect it with a PipedInputStream. Alternatively, we can do this later by using a method provided in PipedOutputStream called connect().

6. OutputStream Buffering

Input and output operations typically involve relatively expensive operations like disk access, network activity, etc. Performing this often can make a program less efficient.

We have “buffered streams” of data in Java to handle these scenarios. BufferedOutputStreamwrites data to a buffer instead which is flushed to the destination less often, when the buffer gets full, or the method flush() is called.

BufferedOutputStream extends FilterOutputStream discussed earlier and wraps an existing OutputStream to write to a destination:

public static void bufferedOutputStream( String file, String ...data) throws IOException { try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file))) { for(String s : data) { out.write(s.getBytes()); out.write(" ".getBytes()); } } }

The critical point to note is that every call to write() for each data argument only writes to the buffer and does not result in a potentially expensive call to the File.

In the case above, if we call this method with data as “Hello”, “World!”, this will only result in data being written to the File when the code exits from the try-with-resources block which calls the method close() on the BufferedOutputStream.

This results in an output file with the following text:

Hello World!

7. Writing Text with OutputStreamWriter

A byte stream, as discussed earlier, represents raw data which may be a bunch of text characters. Now we can get the character array and perform the conversion to the byte array ourselves:

byte[] bytes = data.getBytes();

Java provides convenient classes to bridge this gap. For the case of OutputStream, this class is OutputStreamWriter. OutputStreamWriter wraps an OutputStream and can directly write characters to the desired destination.

We can also optionally provide the OutputStreamWriter with a character set for encoding:

public static void outputStreamWriter(String file, String data) throws IOException { try (OutputStream out = new FileOutputStream(file); Writer writer = new OutputStreamWriter(out,"UTF-8")) { writer.write(data); } }

Ahora, como podemos ver, no tenemos que realizar la transformación de la matriz de caracteres a la matriz de bytes antes de usar FileOutputStream. OutputStreamWriter hace esto convenientemente por nosotros .

No es sorprendente que cuando llamamos al método anterior con datos como "¡Hola mundo!", Esto da como resultado un archivo con texto como:

Hello World!

8. Conclusión

En este artículo, discutimos la clase abstracta de Java OutputStream . Revisamos las interfaces que implementa y los métodos que proporciona.

Luego discutimos algunas de las subclases de OutputStream disponibles en Java. Finalmente hablamos sobre el almacenamiento en búfer y los flujos de caracteres.

Como siempre, el código de los ejemplos está disponible en GitHub.