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
- Stream Definition:
- 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).
- Streams abstract away the details of data transfer, allowing developers to focus on data processing.
- Types of Streams:
- Input Stream: Used to read data.
- Output Stream: Used to write data.
- Data Types:
- Byte Streams: Handle raw binary data (e.g., images, audio).
- 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 Type | Input Class | Output Class |
File | FileInputStream | FileOutputStream |
Array | ByteArrayInputStream | ByteArrayOutputStream |
Piped | PipedInputStream | PipedOutputStream |
2. Character Streams
- Work with text data (16-bit Unicode characters).
- Automatically handle character encoding and decoding.
- Classes: Reader, Writer, and their subclasses.
Stream Type | Input Class | Output Class |
File | FileReader | FileWriter |
Array | CharArrayReader | CharArrayWriter |
String | StringReader | StringWriter |
Piped | PipedReader | PipedWriter |
Hierarchy of Java I/O Streams
- Byte Streams:
- Parent Classes: InputStream and OutputStream.
- Examples: FileInputStream, DataOutputStream, BufferedInputStream.
- Character Streams:
- Parent Classes: Reader and Writer.
- 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.
Type | Buffered Input Class | Buffered Output Class |
Byte Streams | BufferedInputStream | BufferedOutputStream |
Character Streams | BufferedReader | BufferedWriter |
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
- Data Streams:
- Used to read and write primitive data types (e.g., int, double).
- Classes: DataInputStream, DataOutputStream.
- Object Streams:
- Used for reading and writing objects.
- 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
- Abstraction:
- Simplifies I/O operations by abstracting the source and destination details.
- Flexibility:
- Supports a wide range of data sources and destinations, including files, arrays, and sockets.
- Efficiency:
- Buffered streams and specialized streams enhance performance.
Disadvantages of Streams
- Verbose Syntax:
- Requires handling exceptions like IOException, leading to boilerplate code.
- Outdated for Some Use Cases:
- 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.