Skip to content

OutputStream

Unlike InputStream, OutputStream is the most basic output stream provided by the Java standard library.

Similar to InputStream, OutputStream is also an abstract class that serves as the superclass for all output streams. One of the most important methods defined by this abstract class is void write(int b), with the following signature:

java
public abstract void write(int b) throws IOException;

This method writes a byte to the output stream. It's important to note that although an int parameter is passed, only one byte will be written—the lowest 8 bits of the int (effectively, b & 0xff).

Like InputStream, OutputStream also provides a close() method to close the output stream and release system resources. Notably, OutputStream provides a flush() method to force the contents of the buffer to be written out to the destination.

Why is flush() Needed?

When writing data to disk or over the network, for efficiency reasons, the operating system does not write each byte immediately. Instead, it stores the bytes in a memory buffer (essentially a byte[] array) and writes them out all at once when the buffer is full. For many IO devices, writing one byte versus 1000 bytes takes nearly the same amount of time. Therefore, the flush() method allows you to force the buffer's contents to be written out immediately.

In most cases, you don't need to call the flush() method manually, as OutputStream will automatically call it when the buffer is full. Additionally, the close() method will also automatically call flush() before closing the OutputStream.

However, there are certain situations where you must manually call flush(). For example:

If a developer named Xiaoming is creating an online chat application, and after a user types a message, it is written to the network stream using the OutputStream's write() method. During testing, Xiaoming finds that the receiver does not receive any information after the sender types a message.

The reason for this is that writing to the network stream initially writes to the memory buffer, and the buffer will only send its contents once it is full. If the buffer size is 4KB, the sender must type thousands of characters before the operating system sends the buffer's contents, causing the receiver to receive a large amount of messages all at once.

The solution is to call flush() immediately after each message is sent to force the operating system to send the buffer's contents right away.

Buffering in InputStream

It is worth noting that InputStream also uses a buffer. For example, when reading a byte from FileInputStream, the operating system often reads several bytes into the buffer at once and maintains a pointer to the unread portion. Each time we call int read(), it can directly return the next byte from the buffer, avoiding a separate IO operation for every byte. Once the buffer is completely read, calling read() again will trigger the next read operation from the operating system to refill the buffer.

FileOutputStream

Using FileOutputStream, we can demonstrate how to write several bytes to a file stream:

java
public void writeFile() throws IOException {
    OutputStream output = new FileOutputStream("out/readme.txt");
    output.write(72); // H
    output.write(101); // e
    output.write(108); // l
    output.write(108); // l
    output.write(111); // o
    output.close();
}

Writing one byte at a time can be tedious. A more common approach is to write multiple bytes at once using the overloaded method void write(byte[]):

java
public void writeFile() throws IOException {
    OutputStream output = new FileOutputStream("out/readme.txt");
    output.write("Hello".getBytes("UTF-8")); // Hello
    output.close();
}

Similar to InputStream, the above code does not handle the case where exceptions may occur during the process, such as a full disk or lack of write permissions. We need to use try(resource) to ensure that the OutputStream is properly closed regardless of whether an IO error occurs:

java
public void writeFile() throws IOException {
    try (OutputStream output = new FileOutputStream("out/readme.txt")) {
        output.write("Hello".getBytes("UTF-8")); // Hello
    } // The compiler automatically writes the finally block and calls close() here
}

Blocking

Like InputStream, the write() method of OutputStream is also blocking.

OutputStream Implementations

FileOutputStream is commonly used to obtain an output stream from a file, while ByteArrayOutputStream can simulate an OutputStream in memory:

java
import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        byte[] data;
        try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
            output.write("Hello ".getBytes("UTF-8"));
            output.write("world!".getBytes("UTF-8"));
            data = output.toByteArray();
        }
        System.out.println(new String(data, "UTF-8"));
    }
}

ByteArrayOutputStream effectively transforms a byte[] array into an OutputStream in memory. Although it is not commonly used in practical applications, it can be helpful for testing when constructing an OutputStream.

When handling multiple AutoCloseable resources, you can specify multiple resources within the try(resource) statement, separated by a semicolon. For example, reading from one file and writing to another:

java
// Reading input.txt and writing to output.txt:
try (InputStream input = new FileInputStream("input.txt");
     OutputStream output = new FileOutputStream("output.txt"))
{
    input.transferTo(output); // What does transferTo do?
}

Exercise

Please utilize InputStream and OutputStream to write a program that copies a file and can be run with parameters:

bash
java CopyFile.java source.txt copy.txt

Summary

The java.io.OutputStream class in the Java standard library defines the superclass for all output streams:

  • FileOutputStream implements file stream output.
  • ByteArrayOutputStream simulates byte stream output in memory.

In certain situations, it is necessary to manually call the flush() method on OutputStream to force the output of the buffer.

Always use try(resource) to ensure the proper closure of OutputStream.

OutputStream has loaded