Skip to content

Streams in Java

Streams in Java are an abstraction that facilitates the input and output (I/O) of data, such as reading from files, writing to files, or exchanging data between programs. Streams are part of the java.io package and form the backbone of Java’s I/O system.


Core Concept of Streams

  1. Stream Definition:
    1. A stream is a sequence of data. In Java, a stream can represent a flow of data from a source (e.g., file, keyboard, network) to a destination (e.g., console, file, network).
    1. Streams abstract away the details of data transfer, allowing developers to focus on data processing.
  2. Types of Streams:
    1. Input Stream: Used to read data.
    1. Output Stream: Used to write data.
  3. Data Types:
    1. Byte Streams: Handle raw binary data (e.g., images, audio).
    1. Character Streams: Handle text data, working with characters rather than bytes.

Classification of Streams

1. Byte Streams

  • Work with data in byte format (8-bit).
  • Suitable for binary data like images or audio files.
  • Classes: InputStream, OutputStream, and their subclasses.
Stream TypeInput ClassOutput Class
FileFileInputStreamFileOutputStream
ArrayByteArrayInputStreamByteArrayOutputStream
PipedPipedInputStreamPipedOutputStream

2. Character Streams

  • Work with text data (16-bit Unicode characters).
  • Automatically handle character encoding and decoding.
  • Classes: Reader, Writer, and their subclasses.
Stream TypeInput ClassOutput Class
FileFileReaderFileWriter
ArrayCharArrayReaderCharArrayWriter
StringStringReaderStringWriter
PipedPipedReaderPipedWriter

Hierarchy of Java I/O Streams

  1. Byte Streams:
    1. Parent Classes: InputStream and OutputStream.
    1. Examples: FileInputStream, DataOutputStream, BufferedInputStream.
  2. Character Streams:
    1. Parent Classes: Reader and Writer.
    1. Examples: BufferedReader, PrintWriter, FileReader.

Buffered Streams

Buffered streams enhance performance by minimizing direct interaction with the underlying I/O mechanism. They use an internal buffer to temporarily store data.

TypeBuffered Input ClassBuffered Output Class
Byte StreamsBufferedInputStreamBufferedOutputStream
Character StreamsBufferedReaderBufferedWriter

File Handling with Streams

Example: Using Byte Streams

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

public class ByteStreamExample {

    public static void main(String[] args) {

        try (FileInputStream inputStream = new FileInputStream(“input.txt”);

             FileOutputStream outputStream = new FileOutputStream(“output.txt”)) {

            int data;

            while ((data = inputStream.read()) != -1) {

                outputStream.write(data);

            }

            System.out.println(“File copied successfully.”);

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

}


Example: Using Character Streams

import java.io.FileReader;

import java.io.FileWriter;

import java.io.IOException;

public class CharacterStreamExample {

    public static void main(String[] args) {

        try (FileReader reader = new FileReader(“input.txt”);

             FileWriter writer = new FileWriter(“output.txt”)) {

            int data;

            while ((data = reader.read()) != -1) {

                writer.write(data);

            }

            System.out.println(“File copied successfully using character streams.”);

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

}


Specialized Streams

  1. Data Streams:
    1. Used to read and write primitive data types (e.g., int, double).
    1. Classes: DataInputStream, DataOutputStream.
  2. Object Streams:
    1. Used for reading and writing objects.
    1. Classes: ObjectInputStream, ObjectOutputStream.

Example: Writing and Reading Objects

import java.io.*;

class Person implements Serializable {

    String name;

    int age;

    public Person(String name, int age) {

        this.name = name;

        this.age = age;

    }

    @Override

    public String toString() {

        return “Person{name='” + name + “‘, age=” + age + “}”;

    }

}

public class ObjectStreamExample {

    public static void main(String[] args) {

        Person person = new Person(“John”, 25);

        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(“person.ser”));

             ObjectInputStream ois = new ObjectInputStream(new FileInputStream(“person.ser”))) {

            // Write object

            oos.writeObject(person);

            // Read object

            Person readPerson = (Person) ois.readObject();

            System.out.println(“Read object: ” + readPerson);

        } catch (IOException | ClassNotFoundException e) {

            e.printStackTrace();

        }

    }

}


Advantages of Streams

  1. Abstraction:
    1. Simplifies I/O operations by abstracting the source and destination details.
  2. Flexibility:
    1. Supports a wide range of data sources and destinations, including files, arrays, and sockets.
  3. Efficiency:
    1. Buffered streams and specialized streams enhance performance.

Disadvantages of Streams

  1. Verbose Syntax:
    1. Requires handling exceptions like IOException, leading to boilerplate code.
  2. Outdated for Some Use Cases:
    1. Java NIO (java.nio) is more efficient for advanced I/O operations, especially with non-blocking I/O.

Conclusion

Java’s stream API is a powerful tool for handling I/O operations. Whether you are working with files, network sockets, or in-memory data, understanding byte and character streams is crucial. For modern applications, streams often work in tandem with Java NIO for advanced and scalable I/O handling.