Java IO Streams and File Management

IO Streams in Java

In Java, IO streams are used to perform input and output operations. There are two main types of streams: Byte Streams and Character Streams.

1. Byte Streams

Byte Streams are used to handle raw binary data, such as image files, audio files, etc. The most common byte stream classes are InputStream and OutputStream, along with their subclasses.

2. Character Streams

Character Streams are designed to handle data in the form of characters, making them easier to use for text files. The two main classes are Reader and Writer, along with their subclasses.

Aspect Byte Streams Character Streams
Data Type Handles raw binary data such as images, audio, and other non-text files. Handles character data like text files, with support for encoding/decoding.
Classes
  • InputStream (e.g., FileInputStream)
  • OutputStream (e.g., FileOutputStream)
  • Reader (e.g., FileReader)
  • Writer (e.g., FileWriter)
Unit of Data Processes data in bytes (8 bits). Processes data in characters (16 bits in Java, supporting Unicode).
Encoding/Decoding No support for character encoding or decoding. Supports character encoding and decoding (e.g., UTF-8, UTF-16).
Usage
  • Non-text data like images, videos, or binary files.
  • Low-level I/O operations.
  • Text data like documents or strings.
  • Higher-level I/O operations.
Examples
  • Reading an image file.
  • Writing a binary file.
  • Reading a text file.
  • Writing a string to a file.
Performance Faster for binary data. Slower for binary data but appropriate for text data.

Write and Read Files Using Byte Streams

Writing to a file using Byte Stream:


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

public class ByteStreamExample {
    public static void main(String[] args) {
        try (FileOutputStream fos = new FileOutputStream("output.txt")) {
            String data = "This is an example of Byte Stream.";
            fos.write(data.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
      

Reading from a file using Byte Stream:


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

public class ByteStreamExample {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("output.txt")) {
            int byteData;
            while ((byteData = fis.read()) != -1) {
                System.out.print((char) byteData);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
      

Write and Read Files Using Character Streams

Writing to a file using Character Stream:


import java.io.FileWriter;
import java.io.IOException;

public class CharStreamExample {
    public static void main(String[] args) {
        try (FileWriter writer = new FileWriter("output.txt")) {
            String text = "This is an example of Character Stream.";
            writer.write(text);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
      

Reading from a file using Character Stream:


import java.io.FileReader;
import java.io.IOException;

public class CharStreamExample {
    public static void main(String[] args) {
        try (FileReader reader = new FileReader("output.txt")) {
            int charData;
            while ((charData = reader.read()) != -1) {
                System.out.print((char) charData);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
      

Buffered Streams

Buffered Streams provide a way to efficiently read and write data using a buffer. They reduce the number of I/O operations by reading or writing large chunks of data at once. Common buffered classes include BufferedReader, BufferedWriter, BufferedInputStream, and BufferedOutputStream.

BufferedReader and BufferedWriter Example:


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

public class BufferedStreamExample {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new FileReader("output.txt"));
             BufferedWriter bw = new BufferedWriter(new FileWriter("output_copy.txt"))) {
             
            String line;
            while ((line = br.readLine()) != null) {
                bw.write(line);
                bw.newLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
      

Serialization and Deserialization in Java

Serialization is the process of converting an object into a byte stream for storage in a file or transmission over a network. Deserialization is the reverse process of reading the object back from its serialized byte stream.

Key Points

Serialization Example:


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 SerializationExample {
    public static void main(String[] args) {
        Person person = new Person("John Doe", 25);
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
            oos.writeObject(person);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
      

Deserialization Example:


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 DeserializationExample {
    public static void main(String[] args) {
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
            Person person = (Person) ois.readObject();
            System.out.println("Name: " + person.name + ", Age: " + person.age);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
      

Transient Keyword

The transient keyword is used to exclude fields from the serialization process. Fields marked as transient are not included in the serialized object.

Example of Transient Keyword:


import java.io.*;

class Employee implements Serializable {
    String name;
    transient int age;

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

public class TransientExample {
    public static void main(String[] args) {
        Employee emp = new Employee("Alice", 30);
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("employee.ser"))) {
            oos.writeObject(emp);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
      

SerialVersionUID

The serialVersionUID is a unique identifier for a serializable class. It ensures compatibility during deserialization by matching the serialized object's serialVersionUID with the class's serialVersionUID.


class Product implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private double price;

    public Product(String name, double price) {
        this.name = name;
        this.price = price;
    }
}
      

Custom Serialization

Custom serialization allows you to define how an object should be serialized or deserialized by implementing the writeObject and readObject methods.

Custom Serialization Example:


import java.io.*;

class CustomPerson implements Serializable {
    String name;
    int age;

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

    private void writeObject(ObjectOutputStream oos) throws IOException {
        oos.defaultWriteObject();
        oos.writeInt(age + 5); // custom logic during serialization
    }

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        this.age = ois.readInt() - 5; // custom logic during deserialization
    }
}

public class CustomSerializationExample {
    public static void main(String[] args) {
        CustomPerson person = new CustomPerson("Bob", 40);
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("customPerson.ser"))) {
            oos.writeObject(person);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
      

Managing Files Using java.io.File

The java.io.File class provides methods for creating, deleting, and inspecting files and directories in Java. Below are examples of common file management operations using this class.

1. Creating Files and Directories

You can create files and directories using the createNewFile() and mkdir() methods, respectively.

Example: Creating a File


  import java.io.File;
  import java.io.IOException;
  
  public class FileExample {
      public static void main(String[] args) {
          File file = new File("example.txt");
          try {
              if (file.createNewFile()) {
                  System.out.println("File created: " + file.getName());
              } else {
                  System.out.println("File already exists.");
              }
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
  }
        

Example: Creating a Directory


  import java.io.File;
  
  public class DirectoryExample {
      public static void main(String[] args) {
          File directory = new File("exampleDir");
          if (directory.mkdir()) {
              System.out.println("Directory created: " + directory.getName());
          } else {
              System.out.println("Directory already exists.");
          }
      }
  }
        

2. Deleting Files and Directories

Files and directories can be deleted using the delete() method. Note that a directory must be empty to be deleted.

Example: Deleting a File


  import java.io.File;
  
  public class DeleteFileExample {
      public static void main(String[] args) {
          File file = new File("example.txt");
          if (file.delete()) {
              System.out.println("File deleted: " + file.getName());
          } else {
              System.out.println("Failed to delete the file.");
          }
      }
  }
        

Example: Deleting a Directory


  import java.io.File;
  
  public class DeleteDirectoryExample {
      public static void main(String[] args) {
          File directory = new File("exampleDir");
          if (directory.delete()) {
              System.out.println("Directory deleted: " + directory.getName());
          } else {
              System.out.println("Failed to delete the directory. Ensure it is empty.");
          }
      }
  }
        

3. Listing Files and Directories

The list() method returns an array of strings naming the files and directories in the directory represented by the File object.

Example: Listing Files in a Directory


  import java.io.File;
  
  public class ListFilesExample {
      public static void main(String[] args) {
          File directory = new File("exampleDir");
          if (directory.isDirectory()) {
              String[] files = directory.list();
              if (files != null) {
                  System.out.println("Files in " + directory.getName() + ":");
                  for (String file : files) {
                      System.out.println(file);
                  }
              }
          } else {
              System.out.println(directory.getName() + " is not a directory.");
          }
      }
  }
        

4. Checking File Properties

Use methods like exists(), isFile(), isDirectory(), and length() to check file properties.

Example: File Properties


  import java.io.File;
  
  public class FilePropertiesExample {
      public static void main(String[] args) {
          File file = new File("example.txt");
          if (file.exists()) {
              System.out.println("File Name: " + file.getName());
              System.out.println("Absolute Path: " + file.getAbsolutePath());
              System.out.println("Is Writable: " + file.canWrite());
              System.out.println("Is Readable: " + file.canRead());
              System.out.println("File Size: " + file.length() + " bytes");
          } else {
              System.out.println("The file does not exist.");
          }
      }
  }
        

Managing Files Using java.nio.file

The java.nio.file package provides modern and flexible methods for managing files and directories. It includes classes like Path, Paths, and Files that simplify file operations.

1. Creating Files and Directories

Use the Files.createFile() and Files.createDirectory() methods to create files and directories.

Example: Creating a File


  import java.nio.file.*;
  
  public class CreateFileExample {
      public static void main(String[] args) {
          Path filePath = Paths.get("example.txt");
          try {
              Files.createFile(filePath);
              System.out.println("File created: " + filePath.getFileName());
          } catch (FileAlreadyExistsException e) {
              System.out.println("File already exists.");
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
  }
        

Example: Creating a Directory


  import java.nio.file.*;
  
  public class CreateDirectoryExample {
      public static void main(String[] args) {
          Path dirPath = Paths.get("exampleDir");
          try {
              Files.createDirectory(dirPath);
              System.out.println("Directory created: " + dirPath.getFileName());
          } catch (FileAlreadyExistsException e) {
              System.out.println("Directory already exists.");
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
  }
        

2. Deleting Files and Directories

Use the Files.delete() method to delete files and directories. Note that a directory must be empty before it can be deleted.

Example: Deleting a File


  import java.nio.file.*;
  
  public class DeleteFileExample {
      public static void main(String[] args) {
          Path filePath = Paths.get("example.txt");
          try {
              Files.delete(filePath);
              System.out.println("File deleted: " + filePath.getFileName());
          } catch (NoSuchFileException e) {
              System.out.println("File does not exist.");
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
  }
        

Example: Deleting a Directory


  import java.nio.file.*;
  
  public class DeleteDirectoryExample {
      public static void main(String[] args) {
          Path dirPath = Paths.get("exampleDir");
          try {
              Files.delete(dirPath);
              System.out.println("Directory deleted: " + dirPath.getFileName());
          } catch (NoSuchFileException e) {
              System.out.println("Directory does not exist.");
          } catch (DirectoryNotEmptyException e) {
              System.out.println("Directory is not empty.");
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
  }
        

3. Listing Files and Directories

The DirectoryStream interface allows iterating over the entries in a directory.

Example: Listing Files in a Directory


  import java.nio.file.*;
  import java.io.IOException;
  
  public class ListFilesExample {
      public static void main(String[] args) {
          Path dirPath = Paths.get("exampleDir");
          try (DirectoryStream stream = Files.newDirectoryStream(dirPath)) {
              for (Path entry : stream) {
                  System.out.println(entry.getFileName());
              }
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
  }
        

4. Reading and Writing Files

Use Files.readAllLines() and Files.write() for simple file reading and writing operations.

Example: Writing to a File


  import java.nio.file.*;
  import java.io.IOException;
  import java.nio.charset.StandardCharsets;
  
  public class WriteFileExample {
      public static void main(String[] args) {
          Path filePath = Paths.get("example.txt");
          try {
              Files.write(filePath, "Hello, World!".getBytes(StandardCharsets.UTF_8));
              System.out.println("Data written to file.");
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
  }
        

Example: Reading from a File


  import java.nio.file.*;
  import java.io.IOException;
  import java.nio.charset.StandardCharsets;
  import java.util.List;
  
  public class ReadFileExample {
      public static void main(String[] args) {
          Path filePath = Paths.get("example.txt");
          try {
              List lines = Files.readAllLines(filePath, StandardCharsets.UTF_8);
              for (String line : lines) {
                  System.out.println(line);
              }
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
  }
        

5. Checking File Properties

The Files class provides methods like exists(), isReadable(), isWritable(), and size() to check file properties.

Example: Checking File Properties


  import java.nio.file.*;
  
  public class FilePropertiesExample {
      public static void main(String[] args) {
          Path filePath = Paths.get("example.txt");
          try {
              if (Files.exists(filePath)) {
                  System.out.println("File exists.");
                  System.out.println("Readable: " + Files.isReadable(filePath));
                  System.out.println("Writable: " + Files.isWritable(filePath));
                  System.out.println("Size: " + Files.size(filePath) + " bytes");
              } else {
                  System.out.println("File does not exist.");
              }
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
  }