File I/O Part 2

Advanced file operations

Writing Files Efficiently

Modern Java provides multiple efficient ways to write files, each optimized for different scenarios. Choose wisely based on file size, content type, and performance requirements.

Writing Strategies - Complete Guide

Text File Writing - Comprehensive Examples

Complete Text Writing Solutions

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

public class TextFileWriting {

    // Method 1: Write String directly (Java 11+)
    public static void writeString(String filename, String content)
            throws IOException {
        Files.writeString(Path.of(filename), content,
            StandardCharsets.UTF_8);
    }

    // Method 2: BufferedWriter for incremental writing
    public static void writeWithBufferedWriter(String filename)
            throws IOException {
        try (BufferedWriter writer = Files.newBufferedWriter(
                Path.of(filename), StandardCharsets.UTF_8)) {

            writer.write("Header Line");
            writer.newLine();

            for (int i = 0; i < 1000; i++) {
                writer.write("Data line " + i);
                writer.newLine();
            }
            writer.flush();
        }
    }

    // Method 3: Atomic write operation
    public static void writeAtomic(String filename, String content)
            throws IOException {
        Path target = Path.of(filename);
        Path temp = Files.createTempFile("temp", ".tmp");

        try {
            Files.writeString(temp, content);
            Files.move(temp, target, StandardCopyOption.ATOMIC_MOVE);
        } finally {
            Files.deleteIfExists(temp);
        }
    }
}

Performance Comparison

Files.writeString()

Use Case: Small text files

Performance: Fast

BufferedWriter

Use Case: Large text files

Performance: Efficient

Memory-Mapped

Use Case: Large binary files

Performance: Fastest

Serialization and Deserialization - Complete Guide

Serialization converts objects to byte streams for storage or transmission. While Java’s built-in serialization exists, modern alternatives offer better performance, security, and cross-platform compatibility.

Serialization Methods Comparison

MethodProsConsUse Cases
Java SerializationBuilt-in, easy to useSecurity risks, version issuesLegacy systems, quick prototyping
JSON (Jackson/Gson)Human-readable, cross-platformLarger size, slowerAPIs, configuration, web services
Protocol BuffersCompact, fast, versioningRequires schema, complexityMicroservices, gRPC, performance

Java Serialization Implementation

Advanced Serialization

import java.io.*;

class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;
    private transient String password; // Won't be serialized

    // Custom serialization logic
    private void writeObject(ObjectOutputStream oos)
            throws IOException {
        oos.defaultWriteObject();
        oos.writeObject(encrypt(password));
    }
}

public class SerializationExample {
    public static void serializeObject(Object obj, String filename)
            throws IOException {
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream(filename))) {
            oos.writeObject(obj);
        }
    }

    public static <T> T deserializeObject(String filename, Class<T> clazz)
            throws IOException, ClassNotFoundException {
        try (ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream(filename))) {
            return clazz.cast(ois.readObject());
        }
    }
}

JSON Serialization - Modern Approach

JSON with Gson

import com.google.gson.*;

public class JsonSerialization {
    private static final Gson gson = new GsonBuilder()
        .setPrettyPrinting()
        .create();

    public static void serializeToJson(Object obj, String filename)
            throws IOException {
        String json = gson.toJson(obj);
        Files.writeString(Path.of(filename), json);
    }

    public static <T> T deserializeFromJson(String filename, Class<T> clazz)
            throws IOException {
        String json = Files.readString(Path.of(filename));
        return gson.fromJson(json, clazz);
    }
}

Advanced File Operations

File Watching Service

Monitor Directory Changes

import java.nio.file.*;

public class FileWatcher {
    public static void watchDirectory(Path dir) throws Exception {
        WatchService watcher = FileSystems.getDefault().newWatchService();
        dir.register(watcher,
            StandardWatchEventKinds.ENTRY_CREATE,
            StandardWatchEventKinds.ENTRY_MODIFY,
            StandardWatchEventKinds.ENTRY_DELETE);

        while (true) {
            WatchKey key = watcher.take();
            for (WatchEvent<?> event : key.pollEvents()) {
                Path changed = (Path) event.context();
                System.out.println(event.kind() + ": " + changed);
            }
            key.reset();
        }
    }
}

Walking File Trees

Directory Traversal

// Find all Java files
Files.walk(Path.of("src"))
     .filter(p -> p.toString().endsWith(".java"))
     .forEach(System.out::println);

// Copy directory recursively
Files.walk(source)
     .forEach(src -> {
         Path dest = target.resolve(source.relativize(src));
         try {
             Files.copy(src, dest, StandardCopyOption.REPLACE_EXISTING);
         } catch (IOException e) { }
     });

Memory-Mapped Files

High-Performance I/O

try (FileChannel channel = FileChannel.open(Path.of("large.dat"))) {
    long size = channel.size();
    MappedByteBuffer buffer = channel.map(
        FileChannel.MapMode.READ_WRITE, 0, size);

    // Direct memory access - fastest I/O
    for (int i = 0; i < 1000; i++) {
        buffer.putInt(i * 4, i);
    }
    buffer.force(); // Flush to disk
}

File Comparison

Compare Files

// Check if same file
boolean same = Files.isSameFile(path1, path2);

// Find first mismatch (Java 12+)
long mismatch = Files.mismatch(path1, path2); // -1 if identical

// Content comparison
byte[] bytes1 = Files.readAllBytes(path1);
byte[] bytes2 = Files.readAllBytes(path2);
boolean equal = Arrays.equals(bytes1, bytes2);

Best Practices and Performance

Critical Best Practices

Essential Guidelines

  • Always use try-with-resources - Automatic cleanup prevents leaks
  • Specify charset explicitly - Use StandardCharsets.UTF_8
  • Use NIO.2 for new code - Path/Files > File class
  • Buffer your streams - Wrap with Buffered* classes
  • Check file existence before operations - Prevent exceptions
  • Validate user input paths - Prevent directory traversal attacks

Performance Optimization

OperationBest ChoicePerformance Tip
Small text (<100MB)Files.readString()Fastest for one-time read
Large text (>100MB)Files.lines()Stream for memory efficiency
Binary filesFileChannelUse direct buffers (64KB)
Sequential accessBufferedReader/Writer8KB-64KB buffer size
Random accessMemory-mappedBest for large files

Quick Reference

Common Operations Cheat Sheet

// READ
String content = Files.readString(Path.of("file.txt"));
List<String> lines = Files.readAllLines(Path.of("file.txt"));
byte[] bytes = Files.readAllBytes(Path.of("file.bin"));

// WRITE
Files.writeString(Path.of("out.txt"), "content");
Files.write(Path.of("out.txt"), lines);
Files.write(Path.of("out.bin"), bytes);

// COPY/MOVE/DELETE
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
Files.move(source, target, StandardCopyOption.ATOMIC_MOVE);
Files.delete(path);

Memory Efficiency

  • Use streams for large files
  • Direct buffers for binary I/O
  • Memory-mapped for >1GB files

Security

  • Validate all file paths
  • Use secure temp directories
  • Avoid Java serialization

Reliability

  • Handle all IOExceptions
  • Use atomic operations
  • Implement retry logic

Master Java File I/O

Choose the right tool, write efficient code, handle errors gracefully