Skip to content

I/O in Java

Input/Output (I/O) in Java is a mechanism that allows programs to interact with the outside world. This can include reading and writing files, reading from and writing to the console, interacting with network resources, or handling data streams. Java provides a robust and flexible I/O API in the java.io package to work with various I/O operations.

Java I/O can be categorized into two types:

  1. Byte Stream: Used for handling I/O of raw binary data (e.g., images, audio files, etc.).
  2. Character Stream: Used for handling I/O of characters (e.g., text files) and ensures proper encoding and decoding of text.

Byte Streams

Byte streams are used to perform input and output of 8-bit bytes. These are suitable for handling I/O of raw binary data like image files, audio files, etc.

Key Classes in Byte Streams:

  1. InputStream: This is the abstract class for all byte input streams. It has various subclasses like FileInputStream, BufferedInputStream, etc.
  2. OutputStream: This is the abstract class for all byte output streams. It has various subclasses like FileOutputStream, BufferedOutputStream, etc.

Example: Reading and Writing a File Using Byte Streams

java
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class ByteStreamExample {
    public static void main(String[] args) {
        // File to read and write
        String inputFile = "input.txt";
        String outputFile = "output.txt";

        try (FileInputStream fis = new FileInputStream(inputFile);
             FileOutputStream fos = new FileOutputStream(outputFile)) {
            
            int byteData;
            while ((byteData = fis.read()) != -1) {  // Read byte by byte
                fos.write(byteData);  // Write byte by byte
            }
            System.out.println("File copied successfully.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Explanation:

  • FileInputStream: Reads bytes from the specified file.
  • FileOutputStream: Writes bytes to the specified file.
  • The program reads bytes from the input file and writes them one by one to the output file.

Character Streams

Character streams are designed for handling the input and output of 16-bit Unicode characters, and they are particularly useful for text files. These streams ensure proper encoding and decoding of text files, which is important for internationalization.

Key Classes in Character Streams:

  1. Reader: The abstract class for all character input streams. It has subclasses like FileReader, BufferedReader, etc.
  2. Writer: The abstract class for all character output streams. It has subclasses like FileWriter, BufferedWriter, etc.

Example: Reading and Writing Text Files Using Character Streams

java
import java.io.FileReader;
import java.io.FileWriter;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;

public class CharacterStreamExample {
    public static void main(String[] args) {
        String inputFile = "input.txt";
        String outputFile = "output.txt";

        try (BufferedReader br = new BufferedReader(new FileReader(inputFile));
             BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile))) {
            
            String line;
            while ((line = br.readLine()) != null) {
                bw.write(line);  // Write each line to the output file
                bw.newLine();  // Add a newline after each line
            }
            System.out.println("File copied successfully.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Explanation:

  • FileReader: Reads characters from the specified file.
  • FileWriter: Writes characters to the specified file.
  • BufferedReader: Reads text from a character-based input stream, buffering characters to efficiently read characters, arrays, and lines.
  • BufferedWriter: Writes text to a character-based output stream, buffering characters to provide efficient writing of text.

Buffered Streams

Buffered streams provide a performance boost by reducing the number of read/write operations. Instead of reading/writing data byte by byte or character by character, buffered streams use a larger buffer and process data in chunks.

Example: Using Buffered Streams for Efficiency

java
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class BufferedStreamExample {
    public static void main(String[] args) {
        String inputFile = "input.txt";
        String outputFile = "output.txt";

        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(inputFile));
             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(outputFile))) {
            
            int byteData;
            while ((byteData = bis.read()) != -1) {
                bos.write(byteData);  // Write data in buffered chunks
            }
            System.out.println("File copied using buffered streams.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Explanation:

  • BufferedInputStream: Reads data from the input stream in larger chunks.
  • BufferedOutputStream: Writes data to the output stream in larger chunks.
  • Buffered streams help improve performance, especially when reading or writing large files.

Object Streams

Object streams are used for reading and writing Java objects (i.e., serializing and deserializing objects). These streams handle the conversion between Java objects and streams of bytes.

Key Classes in Object Streams:

  1. ObjectInputStream: Deserializes objects (i.e., reads them from a stream).
  2. ObjectOutputStream: Serializes objects (i.e., writes them to a stream).

Example: Writing and Reading Objects Using Object Streams

java
import java.io.*;

class Person implements Serializable {
    String name;
    int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

public class ObjectStreamExample {
    public static void main(String[] args) {
        Person person = new Person("John Doe", 30);
        String fileName = "person.ser";

        // Writing object to a file
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileName))) {
            oos.writeObject(person);
            System.out.println("Object has been serialized.");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // Reading object from a file
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName))) {
            Person deserializedPerson = (Person) ois.readObject();
            System.out.println("Object has been deserialized.");
            System.out.println("Name: " + deserializedPerson.name + ", Age: " + deserializedPerson.age);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

Explanation:

  • Serializable: A marker interface that indicates a class can be serialized.
  • ObjectOutputStream: Writes a Java object to a file (serializes it).
  • ObjectInputStream: Reads a serialized object from a file (deserializes it).

File I/O Operations Using NIO (New I/O)

Java introduced the NIO (New I/O) API in Java 1.4, which provides more efficient and scalable I/O operations. The NIO API is based on buffers and channels and is particularly useful for handling large files, network operations, or I/O intensive tasks.

Key NIO Classes:

  1. Path: Represents a path in the file system.
  2. Files: Contains utility methods for file operations.
  3. FileChannel: Allows reading and writing data to files using channels.
  4. ByteBuffer: A container for binary data in NIO.

Example: Reading and Writing Files Using NIO

java
import java.nio.file.*;
import java.io.IOException;

public class NIOExample {
    public static void main(String[] args) {
        Path inputPath = Paths.get("input.txt");
        Path outputPath = Paths.get("output.txt");

        try {
            // Reading all bytes from input file
            byte[] fileBytes = Files.readAllBytes(inputPath);

            // Writing bytes to output file
            Files.write(outputPath, fileBytes);

            System.out.println("File copied using NIO.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Explanation:

  • Paths.get(): Returns a Path object that represents the file location.
  • Files.readAllBytes(): Reads all bytes from the input file into a byte array.
  • Files.write(): Writes the byte array to the output file.

Conclusion

Java I/O provides a comprehensive set of classes and interfaces for working with data streams, files, and other I/O resources. Some key points to remember:

  • Byte Streams work with raw binary data, and Character Streams work with character data, ensuring proper encoding/decoding.
  • Buffered streams improve I/O performance by reading and writing data in large chunks.
  • Object Streams allow you to serialize and deserialize Java objects.
  • NIO (New I/O) provides more efficient and scalable I/O operations using channels and buffers.

By using the appropriate stream types, you can efficiently handle different types of I/O operations in Java.

J2J Institute private limited