Java Programming File Handling Tutorial
I/O Operations

Java File Handling - Complete Tutorial

Master Java File I/O: Learn to read, write, create, delete files and directories using File, FileReader, FileWriter, BufferedReader, BufferedWriter, FileInputStream, FileOutputStream classes with practical examples.

File Class

File & Directory Operations

Character Streams

Text File Handling

Byte Streams

Binary File Handling

Buffered I/O

High Performance

1. Introduction to Java File Handling

File Handling in Java is crucial for persistent data storage and data exchange between applications. Java provides comprehensive I/O capabilities through the java.io package for reading from and writing to files.

Why File Handling?
  • Data Persistence: Store data beyond program execution
  • Configuration Files: Store application settings
  • Data Logging: Record application events and errors
  • Data Import/Export: Exchange data with other applications
  • Backup & Recovery: Create backup files
  • Large Data Processing: Handle datasets too large for memory
Java I/O Stream Types
  • Byte Streams: Handle binary data (images, videos)
  • Character Streams: Handle text data (UTF-16)
  • Buffered Streams: Improve I/O performance
  • Data Streams: Handle primitive data types
  • Object Streams: Handle object serialization
  • File Streams: File-specific operations

File Handling Flow

Basic file operations follow: Open Stream → Read/Write Data → Close Stream. Always close streams in finally block or use try-with-resources.

File Class

File & directory operations

Character Streams

Text file handling

Byte Streams

Binary file handling

2. The File Class

The java.io.File class represents file and directory pathnames. It provides methods to create, delete, rename files, check file properties, and list directory contents.

FileClassExample.java
import java.io.File;
import java.io.IOException;
import java.util.Date;

public class FileClassExample {
    public static void main(String[] args) {
        // Create File object
        File file = new File("example.txt");
        File directory = new File("testDir");
        
        System.out.println("=== File Information ===");
        System.out.println("File name: " + file.getName());
        System.out.println("Absolute path: " + file.getAbsolutePath());
        System.out.println("Path: " + file.getPath());
        System.out.println("Parent: " + file.getParent());
        System.out.println("Exists: " + file.exists());
        System.out.println("Is file: " + file.isFile());
        System.out.println("Is directory: " + file.isDirectory());
        System.out.println("Is hidden: " + file.isHidden());
        System.out.println("Can read: " + file.canRead());
        System.out.println("Can write: " + file.canWrite());
        System.out.println("Can execute: " + file.canExecute());
        System.out.println("Length: " + file.length() + " bytes");
        System.out.println("Last modified: " + new Date(file.lastModified()));
        
        // File operations
        System.out.println("\n=== File Operations ===");
        try {
            if (file.createNewFile()) {
                System.out.println("File created: " + file.getName());
            } else {
                System.out.println("File already exists.");
            }
            
            // Rename file
            File newFile = new File("renamed_example.txt");
            if (file.renameTo(newFile)) {
                System.out.println("File renamed to: " + newFile.getName());
                file = newFile; // Update reference
            }
            
            // Delete file
            if (file.delete()) {
                System.out.println("File deleted: " + file.getName());
            }
            
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        // Directory operations
        System.out.println("\n=== Directory Operations ===");
        if (directory.mkdir()) {
            System.out.println("Directory created: " + directory.getName());
        }
        
        // List directory contents
        File currentDir = new File(".");
        System.out.println("\nContents of current directory:");
        String[] files = currentDir.list();
        if (files != null) {
            for (String f : files) {
                System.out.println("  " + f);
            }
        }
        
        // Create temporary file
        System.out.println("\n=== Temporary File ===");
        try {
            File tempFile = File.createTempFile("temp", ".txt");
            System.out.println("Temp file created: " + tempFile.getAbsolutePath());
            tempFile.deleteOnExit(); // Delete on JVM exit
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
Method Description Return Type
boolean exists() Checks if file/directory exists boolean
boolean createNewFile() Creates new empty file boolean
boolean delete() Deletes file/directory boolean
boolean mkdir() Creates directory boolean
boolean renameTo(File dest) Renames file/directory boolean
long length() Returns file size in bytes long
String[] list() Lists files in directory String[]
boolean isFile() Checks if it's a file boolean
boolean isDirectory() Checks if it's a directory boolean
long lastModified() Returns last modified timestamp long
File Class Best Practices:
  • Always check exists() before operations
  • Use getAbsolutePath() for full paths
  • Handle SecurityException for permission issues
  • Use deleteOnExit() for temporary files
  • Check return values of file operations
  • Prefer Paths and Files classes (Java NIO) for new code

3. FileReader and FileWriter Classes

FileReader and FileWriter are character streams used for reading and writing text files. They work with characters (16-bit Unicode) rather than bytes.

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

public class FileWriterExample {
    public static void main(String[] args) {
        // FileWriter with automatic resource management (try-with-resources)
        try (FileWriter writer = new FileWriter("output.txt")) {
            
            // Write different types of data
            writer.write("Hello, FileWriter!\n");
            writer.write("This is a text file created using Java.\n");
            writer.write("Current timestamp: " + System.currentTimeMillis() + "\n");
            
            // Write character array
            char[] chars = {'A', 'B', 'C', 'D', 'E', '\n'};
            writer.write(chars);
            
            // Write portion of character array
            writer.write(chars, 1, 3);  // Writes 'B', 'C', 'D'
            writer.write('\n');
            
            // Append mode
            try (FileWriter appendWriter = new FileWriter("output.txt", true)) {
                appendWriter.write("\n--- Appended Content ---\n");
                appendWriter.write("This line was appended to the file.\n");
            }
            
            System.out.println("File written successfully!");
            
        } catch (IOException e) {
            System.out.println("Error writing file: " + e.getMessage());
        }
        
        // Writing with manual exception handling
        System.out.println("\n=== Manual File Writing ===");
        FileWriter manualWriter = null;
        try {
            manualWriter = new FileWriter("manual_output.txt");
            manualWriter.write("Manual file writing example.\n");
            manualWriter.write("Line 2\n");
            manualWriter.write("Line 3\n");
            System.out.println("Manual file written successfully!");
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        } finally {
            try {
                if (manualWriter != null) {
                    manualWriter.close();
                }
            } catch (IOException e) {
                System.out.println("Error closing writer: " + e.getMessage());
            }
        }
    }
}
FileReaderExample.java
import java.io.FileReader;
import java.io.IOException;

public class FileReaderExample {
    public static void main(String[] args) {
        System.out.println("=== Reading File Character by Character ===");
        
        try (FileReader reader = new FileReader("output.txt")) {
            int charCode;
            int charCount = 0;
            
            System.out.println("File content:");
            System.out.println("-------------");
            
            // Read character by character
            while ((charCode = reader.read()) != -1) {
                char character = (char) charCode;
                System.out.print(character);
                charCount++;
            }
            
            System.out.println("\n-------------");
            System.out.println("Total characters read: " + charCount);
            
        } catch (IOException e) {
            System.out.println("Error reading file: " + e.getMessage());
        }
        
        System.out.println("\n=== Reading File into Character Array ===");
        
        try (FileReader reader = new FileReader("output.txt")) {
            char[] buffer = new char[1024];
            int charsRead;
            StringBuilder content = new StringBuilder();
            
            // Read into character array buffer
            while ((charsRead = reader.read(buffer)) != -1) {
                content.append(buffer, 0, charsRead);
            }
            
            System.out.println("File content (using buffer):");
            System.out.println(content.toString());
            
            // Get file statistics
            String fileContent = content.toString();
            System.out.println("\nFile Statistics:");
            System.out.println("Total characters: " + fileContent.length());
            System.out.println("Total lines: " + (fileContent.split("\n").length));
            System.out.println("Total words: " + fileContent.split("\\s+").length);
            
        } catch (IOException e) {
            System.out.println("Error reading file: " + e.getMessage());
        }
        
        System.out.println("\n=== Reading with Manual Exception Handling ===");
        FileReader manualReader = null;
        try {
            manualReader = new FileReader("manual_output.txt");
            char[] smallBuffer = new char[100];
            int count = manualReader.read(smallBuffer);
            
            if (count != -1) {
                System.out.println("First 100 characters:");
                System.out.println(new String(smallBuffer, 0, count));
            }
            
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        } finally {
            try {
                if (manualReader != null) {
                    manualReader.close();
                }
            } catch (IOException e) {
                System.out.println("Error closing reader: " + e.getMessage());
            }
        }
    }
}
Class Constructor Key Methods Use Case
FileWriter FileWriter(String fileName)
FileWriter(String fileName, boolean append)
write(int c)
write(String str)
write(char[] cbuf)
flush()
close()
Writing text files, simple logging
FileReader FileReader(String fileName) read()
read(char[] cbuf)
skip(long n)
close()
Reading text files, configuration files
Important Notes:
  • FileReader uses platform's default charset - may cause encoding issues
  • Always close streams in finally block or use try-with-resources
  • FileWriter may create file if it doesn't exist
  • For better performance, use buffered streams
  • Consider InputStreamReader and OutputStreamWriter for specific charsets

4. BufferedReader and BufferedWriter

BufferedReader and BufferedWriter add buffering capability to character streams, significantly improving I/O performance by reducing the number of physical read/write operations.

BufferedRWExample.java
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class BufferedRWExample {
    public static void main(String[] args) {
        // Writing with BufferedWriter
        System.out.println("=== Writing with BufferedWriter ===");
        try (BufferedWriter writer = new BufferedWriter(new FileWriter("buffered_output.txt"))) {
            
            // Write lines efficiently
            writer.write("=== Student Records ===\n");
            writer.newLine(); // Platform-independent newline
            
            for (int i = 1; i <= 5; i++) {
                String record = String.format("Student %d: Score = %d, Grade = %c", 
                    i, 75 + i * 5, (char)('A' + i % 3));
                writer.write(record);
                writer.newLine();
            }
            
            writer.write("=== End of Records ===");
            writer.flush(); // Ensure all data is written
            
            System.out.println("File written successfully with BufferedWriter");
            
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        // Reading with BufferedReader
        System.out.println("\n=== Reading with BufferedReader ===");
        try (BufferedReader reader = new BufferedReader(new FileReader("buffered_output.txt"))) {
            
            System.out.println("File content (line by line):");
            System.out.println("-----------------------------");
            
            String line;
            int lineNumber = 1;
            
            // Read line by line - most common pattern
            while ((line = reader.readLine()) != null) {
                System.out.printf("%3d: %s\n", lineNumber++, line);
            }
            
            System.out.println("-----------------------------");
            
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        // Advanced BufferedReader features
        System.out.println("\n=== Advanced BufferedReader Operations ===");
        try (BufferedReader reader = new BufferedReader(new FileReader("buffered_output.txt"))) {
            
            // Mark and reset example
            reader.mark(100); // Mark current position
            System.out.println("First 2 lines:");
            for (int i = 0; i < 2; i++) {
                System.out.println(reader.readLine());
            }
            
            reader.reset(); // Go back to marked position
            System.out.println("\nAfter reset - First 2 lines again:");
            for (int i = 0; i < 2; i++) {
                System.out.println(reader.readLine());
            }
            
            // Skip characters
            reader.skip(10);
            System.out.println("\nAfter skipping 10 characters:");
            System.out.println(reader.readLine());
            
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        // Practical example: Read all lines into List
        System.out.println("\n=== Read All Lines into List ===");
        List lines = new ArrayList<>();
        
        try (BufferedReader reader = new BufferedReader(new FileReader("buffered_output.txt"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                lines.add(line);
            }
            
            System.out.println("Total lines read: " + lines.size());
            System.out.println("Lines containing 'Student':");
            lines.stream()
                 .filter(l -> l.contains("Student"))
                 .forEach(System.out::println);
            
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        // Copy file with buffered streams
        System.out.println("\n=== Copy File (Buffered) ===");
        try (BufferedReader source = new BufferedReader(new FileReader("buffered_output.txt"));
             BufferedWriter destination = new BufferedWriter(new FileWriter("copy_output.txt"))) {
            
            String line;
            int lineCount = 0;
            while ((line = source.readLine()) != null) {
                destination.write(line);
                destination.newLine();
                lineCount++;
            }
            
            System.out.println("File copied successfully!");
            System.out.println("Lines copied: " + lineCount);
            
        } catch (IOException e) {
            System.out.println("Error copying file: " + e.getMessage());
        }
    }
}
Advantages of Buffered Streams
  • Performance: Reduces physical I/O operations
  • Line-oriented: readLine() and newLine() methods
  • Mark/Reset: Supports mark/reset operations
  • Efficiency: Better for large files
  • Flexibility: Can wrap any Reader/Writer
  • Standard: Default buffer size (8192 chars) works well
Key Methods
  • readLine() - Reads entire line (BufferedReader)
  • newLine() - Writes line separator (BufferedWriter)
  • mark(int) - Marks current position
  • reset() - Returns to marked position
  • skip(long) - Skips characters
  • flush() - Forces buffer write
BufferedReader Methods Description BufferedWriter Methods Description
String readLine() Reads a line of text void newLine() Writes line separator
boolean ready() Checks if stream is ready to read void flush() Flushes the stream
void mark(int) Marks current position void write(String) Writes a string
void reset() Resets to marked position void write(char[]) Writes character array
long skip(long) Skips characters void write(String, int, int) Writes portion of string

5. FileInputStream and FileOutputStream

FileInputStream and FileOutputStream are byte streams used for reading and writing binary data like images, audio, video, or any raw byte data.

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

public class FileOutputStreamExample {
    public static void main(String[] args) {
        // Writing bytes to file
        System.out.println("=== Writing Bytes with FileOutputStream ===");
        
        try (FileOutputStream fos = new FileOutputStream("binary_data.dat")) {
            
            // Write single bytes
            fos.write(65);  // ASCII 'A'
            fos.write(66);  // ASCII 'B'
            fos.write(67);  // ASCII 'C'
            fos.write(10);  // Newline
            
            // Write byte array
            byte[] data = {68, 69, 70, 71, 72, 10};  // DEFGH + newline
            fos.write(data);
            
            // Write portion of byte array
            byte[] fullData = "Hello Binary World!\n".getBytes();
            fos.write(fullData);
            
            // Write numbers as bytes
            for (int i = 0; i <= 255; i++) {
                fos.write(i);
            }
            
            System.out.println("Binary file created successfully!");
            
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        // Append mode
        System.out.println("\n=== Appending Bytes ===");
        try (FileOutputStream fos = new FileOutputStream("binary_data.dat", true)) {
            fos.write("\n=== Appended Data ===\n".getBytes());
            fos.write("This was appended to the binary file.\n".getBytes());
            System.out.println("Data appended successfully!");
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        // Writing different data types
        System.out.println("\n=== Writing Various Data Types ===");
        try (FileOutputStream fos = new FileOutputStream("mixed_data.dat")) {
            
            // Write string
            String text = "Text data\n";
            fos.write(text.getBytes());
            
            // Write integer (4 bytes)
            int number = 123456;
            fos.write((number >> 24) & 0xFF);
            fos.write((number >> 16) & 0xFF);
            fos.write((number >> 8) & 0xFF);
            fos.write(number & 0xFF);
            fos.write('\n');
            
            // Write double (8 bytes)
            double value = 3.14159;
            long doubleBits = Double.doubleToLongBits(value);
            for (int i = 56; i >= 0; i -= 8) {
                fos.write((int)((doubleBits >> i) & 0xFF));
            }
            fos.write('\n');
            
            System.out.println("Mixed data file created!");
            
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}
FileInputStreamExample.java
import java.io.FileInputStream;
import java.io.IOException;

public class FileInputStreamExample {
    public static void main(String[] args) {
        System.out.println("=== Reading Bytes with FileInputStream ===");
        
        try (FileInputStream fis = new FileInputStream("binary_data.dat")) {
            
            System.out.println("Available bytes: " + fis.available());
            System.out.println("File content as bytes:");
            System.out.println("----------------------");
            
            // Read byte by byte
            int byteValue;
            int byteCount = 0;
            while ((byteValue = fis.read()) != -1) {
                if (byteCount % 16 == 0) {
                    System.out.println();
                    System.out.printf("%04X: ", byteCount);
                }
                
                System.out.printf("%02X ", byteValue);
                byteCount++;
                
                // Show ASCII representation for printable characters
                if (byteCount % 16 == 0) {
                    System.out.print("  ");
                    // To show ASCII, we'd need to buffer the last 16 bytes
                }
            }
            
            System.out.println("\n----------------------");
            System.out.println("Total bytes read: " + byteCount);
            
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        System.out.println("\n=== Reading into Byte Array ===");
        
        try (FileInputStream fis = new FileInputStream("binary_data.dat")) {
            byte[] buffer = new byte[1024];
            int bytesRead;
            int totalBytes = 0;
            
            while ((bytesRead = fis.read(buffer)) != -1) {
                totalBytes += bytesRead;
                
                // Process first 100 bytes as ASCII
                if (totalBytes <= 100) {
                    System.out.println("First " + bytesRead + " bytes as text:");
                    for (int i = 0; i < bytesRead; i++) {
                        char c = (char) buffer[i];
                        if (c >= 32 && c <= 126) { // Printable ASCII
                            System.out.print(c);
                        } else if (c == 10 || c == 13) {
                            System.out.print("\\n");
                        } else {
                            System.out.print('.');
                        }
                    }
                    System.out.println();
                }
            }
            
            System.out.println("Total bytes read (buffered): " + totalBytes);
            
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        System.out.println("\n=== File Copy using Byte Streams ===");
        
        try (FileInputStream source = new FileInputStream("binary_data.dat");
             FileOutputStream destination = new FileOutputStream("copy_binary.dat")) {
            
            byte[] buffer = new byte[4096]; // 4KB buffer
            int bytesRead;
            long totalCopied = 0;
            
            long startTime = System.currentTimeMillis();
            
            while ((bytesRead = source.read(buffer)) != -1) {
                destination.write(buffer, 0, bytesRead);
                totalCopied += bytesRead;
            }
            
            long endTime = System.currentTimeMillis();
            
            System.out.println("File copied successfully!");
            System.out.println("Bytes copied: " + totalCopied);
            System.out.println("Time taken: " + (endTime - startTime) + " ms");
            
        } catch (IOException e) {
            System.out.println("Error copying file: " + e.getMessage());
        }
        
        // Reading with skip and available
        System.out.println("\n=== Using skip() and available() ===");
        
        try (FileInputStream fis = new FileInputStream("binary_data.dat")) {
            System.out.println("Initially available: " + fis.available() + " bytes");
            
            // Skip first 10 bytes
            long skipped = fis.skip(10);
            System.out.println("Skipped " + skipped + " bytes");
            System.out.println("Now available: " + fis.available() + " bytes");
            
            // Read next 5 bytes
            byte[] smallBuffer = new byte[5];
            int read = fis.read(smallBuffer);
            System.out.println("Read " + read + " bytes after skipping:");
            for (int i = 0; i < read; i++) {
                System.out.printf("%02X ", smallBuffer[i]);
            }
            System.out.println();
            
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}
Class Key Methods Description Use Cases
FileInputStream read()
read(byte[] b)
available()
skip(long n)
close()
Reads raw bytes from files Images, audio, video, binary files
FileOutputStream write(int b)
write(byte[] b)
flush()
close()
Writes raw bytes to files Binary file creation, data logging
Byte Stream vs Character Stream:
  • Byte Streams (InputStream/OutputStream): Handle raw binary data, platform-independent
  • Character Streams (Reader/Writer): Handle text data, handle character encoding
  • Use Byte Streams for: Images, audio, video, executable files
  • Use Character Streams for: Text files, configuration files, CSV, JSON
  • Performance: Byte streams are faster for binary data
  • Buffering: Always use buffered versions for better performance

6. Java File Handling Built-in Functions Reference

Complete reference of all important methods and constructors available in Java File Handling classes.

File Class Methods

Method Signature Description Return Type
boolean exists() Tests whether file/directory exists boolean
boolean createNewFile() Atomically creates new empty file boolean
boolean delete() Deletes file/directory boolean
boolean mkdir() Creates directory boolean
boolean mkdirs() Creates directory including parents boolean
boolean renameTo(File dest) Renames file/directory boolean
long length() Returns file size in bytes long
String getName() Returns file/directory name String
String getPath() Returns path string String
String getAbsolutePath() Returns absolute path string String
String getParent() Returns parent directory path String
boolean isFile() Tests if it's a normal file boolean
boolean isDirectory() Tests if it's a directory boolean
boolean isHidden() Tests if file is hidden boolean
boolean canRead() Tests if application can read boolean
boolean canWrite() Tests if application can write boolean
boolean canExecute() Tests if application can execute boolean
long lastModified() Returns last modified timestamp long
String[] list() Lists files in directory String[]
File[] listFiles() Lists files as File objects File[]
static File[] listRoots() Lists available filesystem roots File[]
boolean setReadOnly() Marks file/directory read-only boolean
boolean setWritable(boolean) Sets write permission boolean
boolean setReadable(boolean) Sets read permission boolean
boolean setExecutable(boolean) Sets execute permission boolean

FileReader and FileWriter Constructors

Constructor Description Throws
FileReader(String fileName) Creates FileReader from file name FileNotFoundException
FileReader(File file) Creates FileReader from File object FileNotFoundException
FileReader(FileDescriptor fd) Creates FileReader from FileDescriptor -
FileWriter(String fileName) Creates FileWriter from file name IOException
FileWriter(String fileName, boolean append) Creates FileWriter with append mode IOException
FileWriter(File file) Creates FileWriter from File object IOException
FileWriter(File file, boolean append) Creates FileWriter with append mode IOException
FileWriter(FileDescriptor fd) Creates FileWriter from FileDescriptor -

BufferedReader and BufferedWriter Methods

Class Method Description Return Type
BufferedReader String readLine() Reads a line of text String
boolean ready() Checks if stream is ready to read boolean
void mark(int readAheadLimit) Marks current position void
void reset() Resets stream to marked position void
long skip(long n) Skips characters long
Stream<String> lines() Returns Stream of lines (Java 8+) Stream<String>
BufferedWriter void newLine() Writes line separator void
void write(String s, int off, int len) Writes portion of string void
void flush() Flushes the stream void

FileInputStream and FileOutputStream Methods

Class Method Description Return Type
FileInputStream int read() Reads byte of data int
int read(byte[] b) Reads bytes into array int
int read(byte[] b, int off, int len) Reads bytes into portion of array int
long skip(long n) Skips bytes long
int available() Returns available bytes int
FileOutputStream void write(int b) Writes byte of data void
void write(byte[] b) Writes byte array void
void write(byte[] b, int off, int len) Writes portion of byte array void
FileChannel getChannel() Returns FileChannel object FileChannel

Key Points to Remember

  • Always close streams in finally block or use try-with-resources
  • Use buffered streams for better performance
  • Check file existence before operations
  • Handle exceptions appropriately
  • Use character streams for text, byte streams for binary data
  • Consider Java NIO (Files, Paths) for new code

7. Practical File Handling Examples

Example 1: CSV File Processor

import java.io.*;
import java.util.*;

public class CSVFileProcessor {
    
    public static void writeCSV(String filename, List data) throws IOException {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename))) {
            for (String[] row : data) {
                writer.write(String.join(",", row));
                writer.newLine();
            }
        }
        System.out.println("CSV file created: " + filename);
    }
    
    public static List readCSV(String filename) throws IOException {
        List data = new ArrayList<>();
        try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
            String line;
            while ((line = reader.readLine()) != null) {
                String[] row = line.split(",");
                data.add(row);
            }
        }
        return data;
    }
    
    public static void main(String[] args) {
        // Sample data
        List employeeData = Arrays.asList(
            new String[]{"ID", "Name", "Department", "Salary"},
            new String[]{"1", "John Doe", "Engineering", "75000"},
            new String[]{"2", "Jane Smith", "Marketing", "65000"},
            new String[]{"3", "Bob Johnson", "Sales", "70000"},
            new String[]{"4", "Alice Brown", "HR", "60000"}
        );
        
        try {
            // Write CSV file
            writeCSV("employees.csv", employeeData);
            
            // Read and process CSV
            List readData = readCSV("employees.csv");
            
            System.out.println("\n=== Employee Report ===");
            double totalSalary = 0;
            int dataRows = 0;
            
            for (int i = 0; i < readData.size(); i++) {
                String[] row = readData.get(i);
                if (i == 0) {
                    // Header
                    System.out.printf("%-5s %-15s %-15s %10s\n", 
                        row[0], row[1], row[2], row[3]);
                    System.out.println("------------------------------------------------");
                } else {
                    // Data rows
                    System.out.printf("%-5s %-15s %-15s %10s\n", 
                        row[0], row[1], row[2], row[3]);
                    
                    if (row.length >= 4) {
                        try {
                            totalSalary += Double.parseDouble(row[3]);
                            dataRows++;
                        } catch (NumberFormatException e) {
                            System.out.println("Warning: Invalid salary format for row " + i);
                        }
                    }
                }
            }
            
            System.out.println("------------------------------------------------");
            System.out.printf("Total Employees: %d\n", dataRows);
            System.out.printf("Total Salary: $%.2f\n", totalSalary);
            System.out.printf("Average Salary: $%.2f\n", totalSalary / dataRows);
            
            // Update CSV (add bonus)
            System.out.println("\n=== Adding 10% Bonus ===");
            List updatedData = new ArrayList<>();
            updatedData.add(readData.get(0)); // Header
            
            for (int i = 1; i < readData.size(); i++) {
                String[] row = readData.get(i);
                if (row.length >= 4) {
                    try {
                        double salary = Double.parseDouble(row[3]);
                        double bonus = salary * 0.10;
                        String[] newRow = Arrays.copyOf(row, row.length + 1);
                        newRow[newRow.length - 1] = String.format("%.2f", bonus);
                        updatedData.add(newRow);
                    } catch (NumberFormatException e) {
                        updatedData.add(row);
                    }
                }
            }
            
            // Update header
            updatedData.get(0) = new String[]{"ID", "Name", "Department", "Salary", "Bonus"};
            
            writeCSV("employees_with_bonus.csv", updatedData);
            System.out.println("Updated CSV file created with bonus column!");
            
        } catch (IOException e) {
            System.out.println("Error processing CSV: " + e.getMessage());
        }
    }
}

Example 2: Configuration File Manager

import java.io.*;
import java.util.*;

public class ConfigFileManager {
    private String configFile;
    private Properties properties;
    
    public ConfigFileManager(String configFile) {
        this.configFile = configFile;
        this.properties = new Properties();
    }
    
    public void loadConfig() throws IOException {
        try (FileInputStream fis = new FileInputStream(configFile)) {
            properties.load(fis);
            System.out.println("Configuration loaded from: " + configFile);
        }
    }
    
    public void saveConfig() throws IOException {
        try (FileOutputStream fos = new FileOutputStream(configFile)) {
            properties.store(fos, "Application Configuration");
            System.out.println("Configuration saved to: " + configFile);
        }
    }
    
    public String getProperty(String key) {
        return properties.getProperty(key);
    }
    
    public void setProperty(String key, String value) {
        properties.setProperty(key, value);
    }
    
    public void displayAllProperties() {
        System.out.println("\n=== Current Configuration ===");
        properties.forEach((key, value) -> 
            System.out.printf("%-30s = %s\n", key, value));
    }
    
    public static void main(String[] args) {
        ConfigFileManager configManager = new ConfigFileManager("app_config.properties");
        
        try {
            // Check if config file exists
            File configFile = new File("app_config.properties");
            if (!configFile.exists()) {
                System.out.println("Config file not found. Creating default configuration...");
                
                // Set default properties
                configManager.setProperty("app.name", "MyApplication");
                configManager.setProperty("app.version", "1.0.0");
                configManager.setProperty("database.url", "jdbc:mysql://localhost:3306/mydb");
                configManager.setProperty("database.username", "admin");
                configManager.setProperty("database.password", "secret123");
                configManager.setProperty("server.port", "8080");
                configManager.setProperty("log.level", "INFO");
                configManager.setProperty("cache.enabled", "true");
                configManager.setProperty("max.connections", "100");
                configManager.setProperty("timeout.seconds", "30");
                
                // Save default config
                configManager.saveConfig();
            }
            
            // Load existing config
            configManager.loadConfig();
            configManager.displayAllProperties();
            
            // Update some properties
            System.out.println("\n=== Updating Configuration ===");
            configManager.setProperty("log.level", "DEBUG");
            configManager.setProperty("timeout.seconds", "60");
            
            // Add new property
            configManager.setProperty("feature.new", "enabled");
            
            // Save updated config
            configManager.saveConfig();
            
            // Display updated config
            configManager.loadConfig();
            configManager.displayAllProperties();
            
            // Search for specific properties
            System.out.println("\n=== Database Configuration ===");
            propertiesStartingWith(configManager.properties, "database")
                .forEach((k, v) -> System.out.println(k + " = " + v));
            
            System.out.println("\n=== Application Info ===");
            propertiesStartingWith(configManager.properties, "app")
                .forEach((k, v) -> System.out.println(k + " = " + v));
            
            // Backup configuration
            backupConfig("app_config.properties", 
                        "app_config_backup_" + System.currentTimeMillis() + ".properties");
            
        } catch (IOException e) {
            System.out.println("Error managing configuration: " + e.getMessage());
        }
    }
    
    private static Map propertiesStartingWith(Properties props, String prefix) {
        Map result = new TreeMap<>();
        props.forEach((keyObj, valueObj) -> {
            String key = (String) keyObj;
            if (key.startsWith(prefix + ".")) {
                result.put(key, (String) valueObj);
            }
        });
        return result;
    }
    
    private static void backupConfig(String sourceFile, String backupFile) throws IOException {
        try (FileInputStream fis = new FileInputStream(sourceFile);
             FileOutputStream fos = new FileOutputStream(backupFile)) {
            
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }
            System.out.println("\nConfiguration backed up to: " + backupFile);
        }
    }
}

8. File Handling Best Practices

Common File Handling Mistakes:
  1. Not closing streams: Causes resource leaks
  2. Ignoring exceptions: Empty catch blocks
  3. Hardcoded file paths: Platform dependency
  4. No file existence checks: FileNotFoundException
  5. Large file memory loading: OutOfMemoryError
  6. No buffering: Poor performance
  7. Wrong stream type: Using byte stream for text
File Handling Best Practices
  • Always use try-with-resources (Java 7+)
  • Use buffered streams for better performance
  • Check file existence before operations
  • Handle all I/O exceptions appropriately
  • Use relative paths or configuration
  • Close streams in finally block if not using try-with-resources
  • Use character streams for text, byte streams for binary
Performance Tips
  • Use appropriate buffer size (4KB-8KB)
  • Process large files in chunks
  • Avoid reading entire file into memory
  • Use NIO for high-performance I/O
  • Consider memory-mapped files for random access
  • Use asynchronous I/O for concurrent operations

Choosing the Right File Handling Approach

For text files: BufferedReader/BufferedWriter
For binary files: BufferedInputStream/BufferedOutputStream
For configuration: Properties class with FileInputStream/FileOutputStream
For large files: Process in chunks, use NIO
For temporary files: File.createTempFile() with deleteOnExit()
New projects: Prefer java.nio.file.Files and Paths

Java NIO (New I/O) Recommendation:

For new Java applications, consider using the java.nio package (Files, Paths, FileChannel) which offers:

  • Better performance with buffers and channels
  • Non-blocking I/O operations
  • File system abstraction
  • Atomic operations
  • Better exception handling

Example: Files.readAllLines(Paths.get("file.txt"))