CMP228 Advanced Programming with Java

Advanced Programming with JavaUnit 613 min read

File I/O, Serialization & Object Persistence in Java

Unit 6 of Advanced Programming with Java covers reading/writing files (text, binary), Java’s serialization mechanism, object persistence, and file handling best practices with hands-on examples and error-handling techniques.

TAKEAWAYS:

  • Java’s File and FileInputStream/FileOutputStream classes handle file operations at the byte level, while Scanner/PrintWriter simplify text I/O.
  • Serialization converts objects into byte streams (via Serializable interface) for storage or network transfer, but requires all fields to be serializable.
  • Object persistence (saving/loading objects) uses serialization or databases; serialization is simpler but less flexible for complex queries.
  • File handling errors (e.g., FileNotFoundException, IOException) must be caught explicitly; always close resources using try-with-resources.
  • Binary files (e.g., images) are read/written as byte arrays, while text files use character encodings (UTF-8 by default).
  • Security risks in serialization include malicious object reconstruction; use ObjectInputFilter to mitigate.

1. File I/O Basics: Reading and Writing Files

Java provides two primary approaches to file handling:

  1. Byte-level I/O (for binary files like images, executables):
    • FileInputStream/FileOutputStream for raw bytes.
    • BufferedInputStream/BufferedOutputStream for efficiency.
  2. Character-level I/O (for text files):
    • Scanner (for reading) and PrintWriter (for writing) with File objects.
    • Supports encodings like UTF-8 (Charset.forName("UTF-8")).

Key Classes and Methods

Class/Interface Purpose Example Methods
File Represents file/directory metadata (name, path, size). exists(), createNewFile(), delete()
FileInputStream Reads bytes from a file. read(), read(byte[])
FileOutputStream Writes bytes to a file. write(int), write(byte[])
Scanner Reads text from files/inputs. nextLine(), nextInt(), useDelimiter()
PrintWriter Writes formatted text to files. println(), printf()
BufferedReader/Writer Efficient line-by-line text I/O. readLine(), newLine()

Example: Writing to a Text File

import java.io.PrintWriter;
import java.io.File;
import java.io.IOException;

public class FileWriterExample {
    public static void main(String[] args) {
        try (PrintWriter writer = new PrintWriter(new File("data.txt"), "UTF-8")) {
            writer.println("Hello, File I/O!");
            writer.printf("Current time: %tT%n", System.currentTimeMillis());
        } catch (IOException e) {
            System.err.println("Error writing to file: " + e.getMessage());
        }
    }
}

Trace (State After Each Step):

Step Action File data.txt Content Exception Thrown
Before try Program starts. (Empty) None
PrintWriter init Opens data.txt in write mode. (Empty) None
writer.println() Writes "Hello, File I/O!". Hello, File I/O! None
writer.printf() Writes formatted timestamp. Hello, File I/O!<newline>Current time: ... None
try block ends Auto-closes PrintWriter. (Content preserved) None
catch block (No exception) None

Visual: File Hierarchy

classDiagram
    class File {
        +String name
        +String path
        +boolean exists()
        +boolean createNewFile()
    }
    class FileInputStream {
        +int read()
        +int read(byte[] b)
    }
    class PrintWriter {
        +void println(String)
        +void printf(String, Object...)
    }
    File --> FileInputStream : "Used to create"
    File --> PrintWriter : "Used to create"

2. Reading Binary Files (Images, Executables)

Binary files (e.g., .jpg, .exe) are read/written as byte arrays. Example: Reading an image file.

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

public class BinaryFileReader {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("image.jpg")) {
            byte[] buffer = new byte[fis.available()];
            fis.read(buffer);
            System.out.println("Read " + buffer.length + " bytes.");
        } catch (IOException e) {
            System.err.println("Error: " + e.getMessage());
        }
    }
}

State After Each Step:

Step Action buffer Content Exception Thrown
Before try Program starts. null None
FileInputStream Opens image.jpg. null None
fis.available() Returns file size (e.g., 1024 bytes). null None
fis.read(buffer) Fills buffer with file bytes. [byte[1024] of image data] None
try block ends Auto-closes FileInputStream. [byte[1024]] (preserved) None

Real-World Example: Daraz Order Processing Daraz stores user order data (e.g., product IDs, quantities) in binary files for faster access during peak hours. Binary files reduce parsing overhead compared to text files.


3. Serialization: Saving Objects to Files

Serialization converts an object’s state into a byte stream, enabling persistence or network transfer. Requires implementing Serializable.

Key Interfaces and Classes

Interface/Class Purpose
Serializable Marker interface (no methods); enables serialization.
ObjectOutputStream Writes serialized objects to a stream.
ObjectInputStream Reads serialized objects from a stream.
transient keyword Marks fields to exclude from serialization.

Example: Serializing a Student Object

import java.io.*;

class Student implements Serializable {
    private String name;
    private transient int password; // Excluded from serialization
    private static final long serialVersionUID = 1L; // For version control

    public Student(String name, int password) {
        this.name = name;
        this.password = password;
    }
}

public class SerializationExample {
    public static void main(String[] args) {
        Student student = new Student("Rohan", 12345);

        // Serialize to file
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("student.ser"))) {
            oos.writeObject(student);
            System.out.println("Serialized data is saved in student.ser");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // Deserialize from file
        try (ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("student.ser"))) {
            Student loadedStudent = (Student) ois.readObject();
            System.out.println("Deserialized: " + loadedStudent.name);
            // password remains 0 (transient)
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

State After Serialization:

graph LR
    A["Student Object"] -->|"serialized to"| B["student.ser (byte stream)"]
    B -->|"deserialized into"| C["New Student Object"]
    C --> D["name: Rohan"]
    C --> E["password: 0 (transient)"]

Visual: Serialization Process

sequenceDiagram
    participant Student as Student Object
    participant OOS as ObjectOutputStream
    participant File as student.ser
    participant OIS as ObjectInputStream
    participant NewStudent as Deserialized Object

    Student->>OOS: writeObject()
    OOS->>File: Writes byte stream
    File-->>OIS: Reads byte stream
    OIS->>NewStudent: readObject()
    Note right of NewStudent: password is 0 (transient)

4. Object Persistence: Serialization vs. Databases

Feature Serialization Database (e.g., JDBC)
Use Case Simple object storage/retrieval. Complex queries, relationships.
Performance Fast for single objects. Slower for simple CRUD.
Flexibility Limited (no SQL queries). Full query support (JOIN, WHERE).
Security Vulnerable to malicious data. ACID-compliant (transactions).
Example Saving game progress. Banking transactions (Nepal Rastra Bank).

Real-World Example: eSewa Transaction Logs eSewa serializes transaction records (e.g., Transaction{userID, amount, timestamp}) to files for offline processing. During high traffic, these files are later merged into a database for reporting.


5. Handling File I/O Exceptions

Common exceptions and best practices:

  • FileNotFoundException: File does not exist. Fix: Check file path or create the file first.
  • IOException: General I/O errors (permission, disk full). Fix: Use try-with-resources to auto-close streams.
  • ClassNotFoundException: Deserializing unknown classes. Fix: Ensure serialVersionUID matches.

Example: Robust File Reading

import java.io.*;

public class RobustFileReader {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(
                new FileReader("data.txt"))) {
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (FileNotFoundException e) {
            System.err.println("File not found. Creating a new one...");
            try (PrintWriter pw = new PrintWriter("data.txt")) {
                pw.println("Default content");
            }
        } catch (IOException e) {
            System.err.println("Error reading file: " + e.getMessage());
        }
    }
}

6. Advanced Topics: Custom Serialization

Override writeObject()/readObject() for control over serialization:

class BankAccount implements Serializable {
    private String accountNumber;
    private transient double balance; // Encrypted in file

    private void writeObject(ObjectOutputStream oos) throws IOException {
        oos.defaultWriteObject();
        oos.writeDouble(encrypt(balance)); // Custom encryption
    }

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        this.balance = decrypt(ois.readDouble());
    }

    private double encrypt(double value) { /* ... */ }
    private double decrypt(double value) { /* ... */ }
}

Visual: Custom Serialization Flow

flowchart TD
    A["BankAccount Object"] --> B["writeObject()"]
    B --> C["Encrypt balance"]
    C --> D["Write to stream"]
    D --> E["File: encrypted.ser"]
    E --> F["readObject()"]
    F --> G["Decrypt balance"]
    G --> H["New BankAccount Object"]

In the Real World

  1. Khalti Payment Records

    • Idea Used: Serialization
    • How: Khalti stores completed transactions (e.g., Transaction{id, amount, merchant}) as serialized objects in temporary files during peak hours (e.g., Dashain sales). These files are later merged into a database for reconciliation.
  2. Nepal Telecom (NTC) Call Logs

    • Idea Used: Binary File I/O
    • How: NTC’s billing system reads binary call detail records (CDRs) from switches. Each CDR is a fixed-size binary block containing callerID, duration, and timestamp. Binary format minimizes storage and speeds up processing for millions of daily calls.
  3. Pathao Driver Availability

    • Idea Used: File I/O + Serialization
    • How: Pathao’s backend serializes driver availability status (e.g., Driver{id, location, isAvailable}) to disk when the server restarts. This ensures drivers are quickly matched to riders without losing state during crashes.

Exam Tip

  1. File Paths: Always use absolute paths in exams (e.g., C:\\data\\file.txt or /home/user/file.txt). Relative paths may fail in automated grading.
  2. Resource Management: Prefer try-with-resources over manual close() calls. Examiners check for auto-closing.
  3. Serialization Pitfalls:
    • Forgetting implements Serializable → NotSerializableException.
    • Missing serialVersionUID → InvalidClassException during deserialization.
    • transient fields are excluded; explain why in answers.
  4. Error Handling: Exams often test exception handling. Always catch IOException and ClassNotFoundException for serialization.
  5. Binary vs. Text Files:
    • Binary: Use FileInputStream/FileOutputStream + byte arrays.
    • Text: Use Scanner/PrintWriter with encodings (e.g., UTF-8).
  6. Worked Examples: Draw state diagrams (like the ones above) for:
    • File content after each write()/read().
    • Object graph before/after serialization.
  7. Real-World Mapping: Relate examples to local apps (e.g., "How would you serialize a Daraz order for offline processing?").

Based on the PU BE Computer (PU) syllabus for Advanced Programming with Java (CMP228), unit 6.

Discussion

Loading…