Skip to main content
  1. Resources/
  2. Study Materials/
  3. Information & Communication Technology Engineering/
  4. ICT Semester 4/
  5. Java Programming (4343203)/

13 mins· ·
Milav Dabgar
Author
Milav Dabgar
Experienced lecturer in the electrical and electronic manufacturing industry. Skilled in Embedded Systems, Image Processing, Data Science, MATLAB, Python, STM32. Strong education professional with a Master’s degree in Communication Systems Engineering from L.D. College of Engineering - Ahmedabad.
Java Programming - File Handling

Java Programming Language

Chapter 11: File Handling

Input/Output Operations in Java


Course: 4343203 - Java Programming

What We'll Cover

  • File and Directory Operations
  • Stream-based I/O
  • Character and Byte Streams
  • Buffered I/O
  • Object Serialization
  • NIO (New I/O) Overview
  • File Exception Handling
  • Best Practices

Java I/O Stream Hierarchy

Java I/O Streams Hierarchy and Usage

File and Directory Operations

File class provides methods for file and directory manipulation

File Class Basics

Creating File Objects:


import java.io.File;

public class FileBasics {
    public static void main(String[] args) {
        // Different ways to create File objects
        File file1 = new File("data.txt");
        File file2 = new File("/home/user/documents/data.txt");
        File file3 = new File("C:\\Users\\User\\data.txt");
        
        // Using parent directory and filename
        File parentDir = new File("/home/user/documents");
        File file4 = new File(parentDir, "data.txt");
        
        // Using File.separator for platform independence
        String path = "documents" + File.separator + "data.txt";
        File file5 = new File(path);
        
        System.out.println("File separator: " + File.separator);
        System.out.println("Path separator: " + File.pathSeparator);
        
        // File information
        System.out.println("Absolute path: " + file1.getAbsolutePath());
        System.out.println("Parent: " + file1.getParent());
        System.out.println("Name: " + file1.getName());
    }
}
                            

File Properties and Tests:


public class FileProperties {
    public static void testFile(String filename) {
        File file = new File(filename);
        
        System.out.println("File: " + filename);
        System.out.println("Exists: " + file.exists());
        System.out.println("Is file: " + file.isFile());
        System.out.println("Is directory: " + file.isDirectory());
        System.out.println("Can read: " + file.canRead());
        System.out.println("Can write: " + file.canWrite());
        System.out.println("Can execute: " + file.canExecute());
        System.out.println("Is hidden: " + file.isHidden());
        
        if (file.exists()) {
            System.out.println("Size: " + file.length() + " bytes");
            System.out.println("Last modified: " + 
                new Date(file.lastModified()));
        }
        
        System.out.println("------------------------");
    }
    
    public static void main(String[] args) {
        testFile("data.txt");
        testFile("documents");
        testFile("nonexistent.txt");
    }
}
                            

File and Directory Operations

Creating Files and Directories:


import java.io.*;

public class FileOperations {
    public static void main(String[] args) {
        try {
            // Create a new file
            File newFile = new File("example.txt");
            if (newFile.createNewFile()) {
                System.out.println("File created: " + newFile.getName());
            } else {
                System.out.println("File already exists");
            }
            
            // Create directory
            File dir = new File("myDirectory");
            if (dir.mkdir()) {
                System.out.println("Directory created: " + dir.getName());
            }
            
            // Create directory with parent directories
            File nestedDir = new File("parent/child/grandchild");
            if (nestedDir.mkdirs()) {
                System.out.println("Nested directories created");
            }
            
            // Create temporary file
            File tempFile = File.createTempFile("temp", ".txt");
            System.out.println("Temp file: " + tempFile.getAbsolutePath());
            tempFile.deleteOnExit(); // Delete when JVM exits
            
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}
                            

Listing Directory Contents:


public class DirectoryListing {
    public static void listDirectory(String dirPath) {
        File directory = new File(dirPath);
        
        if (!directory.exists()) {
            System.out.println("Directory doesn't exist: " + dirPath);
            return;
        }
        
        if (!directory.isDirectory()) {
            System.out.println("Not a directory: " + dirPath);
            return;
        }
        
        System.out.println("Contents of: " + directory.getAbsolutePath());
        
        // List all files and directories
        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                String type = file.isDirectory() ? "[DIR]" : "[FILE]";
                System.out.println(type + " " + file.getName());
            }
        }
        
        // List only files with specific extension
        File[] txtFiles = directory.listFiles((dir, name) -> 
            name.toLowerCase().endsWith(".txt"));
        
        System.out.println("\n.txt files:");
        if (txtFiles != null) {
            for (File file : txtFiles) {
                System.out.println(file.getName());
            }
        }
    }
    
    public static void main(String[] args) {
        listDirectory(".");
        listDirectory("documents");
    }
}
                            

Stream-based I/O

Streams provide a unified way to handle input and output operations

Stream Types

Stream Categories:

  • Byte Streams: Handle binary data (8-bit bytes)
  • Character Streams: Handle text data (16-bit Unicode)
  • Input Streams: Read data from source
  • Output Streams: Write data to destination

Byte Streams

  • InputStream: Abstract base class
  • OutputStream: Abstract base class
  • FileInputStream: Read from files
  • FileOutputStream: Write to files
  • BufferedInputStream: Buffered reading
  • BufferedOutputStream: Buffered writing

Character Streams

  • Reader: Abstract base class
  • Writer: Abstract base class
  • FileReader: Read text from files
  • FileWriter: Write text to files
  • BufferedReader: Buffered text reading
  • BufferedWriter: Buffered text writing

Byte Stream Example

Writing Bytes:


import java.io.*;

public class ByteStreamWrite {
    public static void main(String[] args) {
        String data = "Hello, File I/O!";
        
        // Using FileOutputStream
        try (FileOutputStream fos = new FileOutputStream("data.bin")) {
            byte[] bytes = data.getBytes();
            fos.write(bytes);
            System.out.println("Data written to file");
            
        } catch (IOException e) {
            System.out.println("Error writing file: " + e.getMessage());
        }
        
        // Using BufferedOutputStream for efficiency
        try (BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream("buffered_data.bin"))) {
            
            for (int i = 0; i < 1000; i++) {
                bos.write(("Line " + i + "\n").getBytes());
            }
            bos.flush(); // Ensure all data is written
            System.out.println("Buffered data written");
            
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}
                            

Reading Bytes:


public class ByteStreamRead {
    public static void main(String[] args) {
        // Reading with FileInputStream
        try (FileInputStream fis = new FileInputStream("data.bin")) {
            int byteData;
            System.out.print("File content: ");
            while ((byteData = fis.read()) != -1) {
                System.out.print((char) byteData);
            }
            System.out.println();
            
        } catch (IOException e) {
            System.out.println("Error reading file: " + e.getMessage());
        }
        
        // Reading chunks of data
        try (FileInputStream fis = new FileInputStream("data.bin")) {
            byte[] buffer = new byte[1024];
            int bytesRead;
            
            while ((bytesRead = fis.read(buffer)) != -1) {
                String content = new String(buffer, 0, bytesRead);
                System.out.print(content);
            }
            
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        // Using BufferedInputStream
        try (BufferedInputStream bis = new BufferedInputStream(
                new FileInputStream("buffered_data.bin"))) {
            
            int data;
            int lineCount = 0;
            while ((data = bis.read()) != -1) {
                if ((char) data == '\n') {
                    lineCount++;
                }
            }
            System.out.println("Lines read: " + lineCount);
            
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}
                            

Character Streams

Character streams are optimized for text data handling

Text File Operations

Writing Text Files:


import java.io.*;

public class TextFileWrite {
    public static void main(String[] args) {
        // Using FileWriter
        try (FileWriter writer = new FileWriter("sample.txt")) {
            writer.write("Hello, World!\n");
            writer.write("This is a text file.\n");
            writer.write("Java File I/O is powerful!");
            System.out.println("Text written to file");
            
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        // Using BufferedWriter for better performance
        try (BufferedWriter bufferedWriter = new BufferedWriter(
                new FileWriter("large_file.txt"))) {
            
            for (int i = 1; i <= 10000; i++) {
                bufferedWriter.write("Line number: " + i);
                bufferedWriter.newLine(); // Platform-independent newline
            }
            System.out.println("Large file written with BufferedWriter");
            
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        // Using PrintWriter for formatted output
        try (PrintWriter printWriter = new PrintWriter("formatted.txt")) {
            printWriter.println("Student Report");
            printWriter.println("==============");
            printWriter.printf("Name: %s%n", "John Doe");
            printWriter.printf("Grade: %.2f%n", 87.5);
            printWriter.printf("Attendance: %d%%%n", 95);
            
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}
                            

Reading Text Files:


public class TextFileRead {
    public static void main(String[] args) {
        // Using FileReader
        try (FileReader reader = new FileReader("sample.txt")) {
            int character;
            System.out.print("File content: ");
            while ((character = reader.read()) != -1) {
                System.out.print((char) character);
            }
            System.out.println();
            
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        // Using BufferedReader for line-by-line reading
        try (BufferedReader bufferedReader = new BufferedReader(
                new FileReader("sample.txt"))) {
            
            String line;
            int lineNumber = 1;
            System.out.println("\nLine by line:");
            while ((line = bufferedReader.readLine()) != null) {
                System.out.println(lineNumber + ": " + line);
                lineNumber++;
            }
            
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        // Reading entire file into a String
        try (BufferedReader reader = new BufferedReader(
                new FileReader("sample.txt"))) {
            
            StringBuilder content = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                content.append(line).append("\n");
            }
            
            System.out.println("Entire file content:");
            System.out.println(content.toString());
            
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}
                            

File Copy Example


import java.io.*;

public class FileCopyExample {
    
    // Copy using byte streams
    public static void copyFileBytes(String source, String destination) {
        try (FileInputStream fis = new FileInputStream(source);
             FileOutputStream fos = new FileOutputStream(destination)) {
            
            byte[] buffer = new byte[1024];
            int bytesRead;
            
            while ((bytesRead = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }
            
            System.out.println("File copied successfully (byte streams)");
            
        } catch (IOException e) {
            System.out.println("Error copying file: " + e.getMessage());
        }
    }
    
    // Copy using character streams (for text files)
    public static void copyFileChars(String source, String destination) {
        try (BufferedReader reader = new BufferedReader(new FileReader(source));
             BufferedWriter writer = new BufferedWriter(new FileWriter(destination))) {
            
            String line;
            while ((line = reader.readLine()) != null) {
                writer.write(line);
                writer.newLine();
            }
            
            System.out.println("Text file copied successfully (character streams)");
            
        } catch (IOException e) {
            System.out.println("Error copying file: " + e.getMessage());
        }
    }
    
    // Copy with progress indication
    public static void copyFileWithProgress(String source, String destination) {
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source));
             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destination))) {
            
            File sourceFile = new File(source);
            long totalBytes = sourceFile.length();
            long copiedBytes = 0;
            
            byte[] buffer = new byte[8192];
            int bytesRead;
            
            while ((bytesRead = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, bytesRead);
                copiedBytes += bytesRead;
                
                // Show progress
                int progress = (int) ((copiedBytes * 100) / totalBytes);
                System.out.print("\rProgress: " + progress + "%");
            }
            
            System.out.println("\nFile copied with progress tracking");
            
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
    
    public static void main(String[] args) {
        copyFileBytes("source.txt", "destination_bytes.txt");
        copyFileChars("source.txt", "destination_chars.txt");
        copyFileWithProgress("large_file.txt", "large_file_copy.txt");
    }
}
                    

Object Serialization

Serialization converts objects to byte streams for storage or transmission

Serializable Objects

Creating Serializable Class:


import java.io.*;

// Serializable class
class Student implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private String name;
    private int age;
    private double grade;
    private transient String password; // Won't be serialized
    
    public Student(String name, int age, double grade, String password) {
        this.name = name;
        this.age = age;
        this.grade = grade;
        this.password = password;
    }
    
    // Getters and setters
    public String getName() { return name; }
    public int getAge() { return age; }
    public double getGrade() { return grade; }
    public String getPassword() { return password; }
    
    @Override
    public String toString() {
        return "Student{name='" + name + "', age=" + age + 
               ", grade=" + grade + ", password='" + password + "'}";
    }
}

// Non-serializable field example
class Course implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private String courseName;
    private Student[] students;
    private static String university = "XYZ University"; // Static fields not serialized
    
    public Course(String courseName, Student[] students) {
        this.courseName = courseName;
        this.students = students;
    }
    
    public String getCourseName() { return courseName; }
    public Student[] getStudents() { return students; }
    public static String getUniversity() { return university; }
}
                            

Serialization Example:


public class SerializationExample {
    
    // Serialize object to file
    public static void serializeStudent(Student student, String filename) {
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream(filename))) {
            
            oos.writeObject(student);
            System.out.println("Student serialized to " + filename);
            
        } catch (IOException e) {
            System.out.println("Serialization error: " + e.getMessage());
        }
    }
    
    // Deserialize object from file
    public static Student deserializeStudent(String filename) {
        try (ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream(filename))) {
            
            Student student = (Student) ois.readObject();
            System.out.println("Student deserialized from " + filename);
            return student;
            
        } catch (IOException | ClassNotFoundException e) {
            System.out.println("Deserialization error: " + e.getMessage());
            return null;
        }
    }
    
    public static void main(String[] args) {
        // Create and serialize student
        Student student = new Student("Alice", 20, 85.5, "secret123");
        System.out.println("Original: " + student);
        
        serializeStudent(student, "student.ser");
        
        // Deserialize student
        Student deserializedStudent = deserializeStudent("student.ser");
        if (deserializedStudent != null) {
            System.out.println("Deserialized: " + deserializedStudent);
            // Note: password will be null (transient field)
        }
    }
}
                            

Advanced Serialization

Custom Serialization:


class BankAccount implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private String accountNumber;
    private String holderName;
    private transient double balance; // Don't serialize balance directly
    
    public BankAccount(String accountNumber, String holderName, double balance) {
        this.accountNumber = accountNumber;
        this.holderName = holderName;
        this.balance = balance;
    }
    
    // Custom serialization method
    private void writeObject(ObjectOutputStream oos) throws IOException {
        oos.defaultWriteObject(); // Serialize non-transient fields
        
        // Encrypt balance before serialization
        double encryptedBalance = balance * 1.5 + 1000; // Simple encryption
        oos.writeDouble(encryptedBalance);
    }
    
    // Custom deserialization method
    private void readObject(ObjectInputStream ois) 
            throws IOException, ClassNotFoundException {
        ois.defaultReadObject(); // Deserialize non-transient fields
        
        // Decrypt balance after deserialization
        double encryptedBalance = ois.readDouble();
        this.balance = (encryptedBalance - 1000) / 1.5; // Simple decryption
    }
    
    public double getBalance() { return balance; }
    
    @Override
    public String toString() {
        return "BankAccount{accountNumber='" + accountNumber + 
               "', holderName='" + holderName + "', balance=" + balance + "}";
    }
}
                            

Serialization Best Practices:

Do's:
  • Always declare serialVersionUID
  • Use transient for sensitive data
  • Implement custom serialization when needed
  • Handle version compatibility
  • Close streams properly
Don'ts:
  • Don't serialize sensitive information
  • Don't serialize large objects unnecessarily
  • Don't ignore ClassNotFoundException
  • Don't serialize without versioning

Testing Custom Serialization:


public class CustomSerializationTest {
    public static void main(String[] args) {
        BankAccount account = new BankAccount(
            "123456789", "John Doe", 1500.75);
        
        System.out.println("Original: " + account);
        
        // Serialize
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("account.ser"))) {
            oos.writeObject(account);
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // Deserialize
        try (ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("account.ser"))) {
            BankAccount restoredAccount = (BankAccount) ois.readObject();
            System.out.println("Restored: " + restoredAccount);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
                            

File I/O Exception Handling

Proper exception handling is crucial for robust file operations

Common File Exceptions

Exception Types:

  • FileNotFoundException: File doesn't exist
  • IOException: General I/O error
  • SecurityException: Access denied
  • OutOfMemoryError: Large file operations
  • ClassNotFoundException: Deserialization error

Robust File Operations:


public class SafeFileOperations {
    
    public static String readFileContent(String filename) {
        StringBuilder content = new StringBuilder();
        
        try (BufferedReader reader = new BufferedReader(
                new FileReader(filename))) {
            
            String line;
            while ((line = reader.readLine()) != null) {
                content.append(line).append("\n");
            }
            
        } catch (FileNotFoundException e) {
            System.err.println("File not found: " + filename);
            return null;
        } catch (IOException e) {
            System.err.println("Error reading file: " + e.getMessage());
            return null;
        }
        
        return content.toString();
    }
    
    public static boolean writeFileContent(String filename, String content) {
        try (BufferedWriter writer = new BufferedWriter(
                new FileWriter(filename))) {
            
            writer.write(content);
            return true;
            
        } catch (IOException e) {
            System.err.println("Error writing file: " + e.getMessage());
            return false;
        }
    }
}
                            

Try-with-Resources Best Practice:


public class ResourceManagement {
    
    // Bad practice - manual resource management
    public static void badExample(String filename) {
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(filename));
            String line = reader.readLine();
            System.out.println(line);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    // Good practice - try-with-resources
    public static void goodExample(String filename) {
        try (BufferedReader reader = new BufferedReader(
                new FileReader(filename))) {
            
            String line = reader.readLine();
            System.out.println(line);
            // Reader automatically closed
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    // Multiple resources
    public static void copyFile(String source, String destination) {
        try (BufferedReader reader = new BufferedReader(new FileReader(source));
             BufferedWriter writer = new BufferedWriter(new FileWriter(destination))) {
            
            String line;
            while ((line = reader.readLine()) != null) {
                writer.write(line);
                writer.newLine();
            }
            
        } catch (IOException e) {
            System.err.println("Error copying file: " + e.getMessage());
        }
    }
}
                            

NIO (New I/O) Overview

Java NIO provides more efficient and flexible I/O operations

NIO vs Traditional I/O

FeatureTraditional I/ONIO
ApproachStream-orientedBuffer-oriented
BlockingBlocking I/ONon-blocking I/O
PerformanceGood for small filesBetter for large files
Memory UsageDirect to JVM heapDirect memory access
File OperationsLimited file operationsRich file operations

Simple NIO Example:


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

public class NIOExample {
    public static void main(String[] args) {
        try {
            // Read entire file
            Path path = Paths.get("sample.txt");
            String content = Files.readString(path);
            System.out.println(content);
            
            // Write to file
            String data = "Hello NIO!";
            Files.writeString(path, data);
            
            // Copy file
            Files.copy(path, Paths.get("copy.txt"), 
                StandardCopyOption.REPLACE_EXISTING);
            
            // Check file properties
            System.out.println("File size: " + Files.size(path));
            System.out.println("Is readable: " + Files.isReadable(path));
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
                            

Directory Operations with NIO:


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

public class NIODirectories {
    public static void main(String[] args) {
        try {
            Path dir = Paths.get("testdir");
            
            // Create directory
            Files.createDirectories(dir);
            
            // List directory contents
            Files.list(dir).forEach(System.out::println);
            
            // Walk directory tree
            Files.walk(dir)
                 .filter(Files::isRegularFile)
                 .forEach(System.out::println);
            
            // Find files by pattern
            Files.find(dir, 2, 
                (path, attrs) -> path.toString().endsWith(".txt"))
                .forEach(System.out::println);
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
                            

File I/O Best Practices

✅ Best Practices

  • Use try-with-resources for automatic cleanup
  • Use buffered streams for better performance
  • Handle specific exceptions appropriately
  • Use character streams for text files
  • Use byte streams for binary files
  • Check file existence before operations
  • Use platform-independent file separators
  • Consider NIO for large file operations

⚠️ Common Pitfalls

  • Forgetting to close streams
  • Not handling FileNotFoundException
  • Using wrong stream type for data
  • Ignoring character encoding issues
  • Not checking file permissions
  • Loading entire large files into memory
  • Hardcoding file paths
  • Not validating user-provided file paths

Chapter Summary

File Operations:

  • File and directory manipulation
  • Stream-based I/O (byte and character)
  • Buffered I/O for performance
  • Object serialization
  • Exception handling in file operations

Practical Skills:

  • Reading and writing files safely
  • Copying and moving files
  • Serializing objects for persistence
  • Using try-with-resources
  • Understanding NIO basics

Next: Collections Framework

Thank You!

Questions?


Ready to explore Collections Framework!