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
| Method | Pros | Cons | Use Cases |
|---|---|---|---|
| Java Serialization | Built-in, easy to use | Security risks, version issues | Legacy systems, quick prototyping |
| JSON (Jackson/Gson) | Human-readable, cross-platform | Larger size, slower | APIs, configuration, web services |
| Protocol Buffers | Compact, fast, versioning | Requires schema, complexity | Microservices, 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
| Operation | Best Choice | Performance Tip |
|---|---|---|
| Small text (<100MB) | Files.readString() | Fastest for one-time read |
| Large text (>100MB) | Files.lines() | Stream for memory efficiency |
| Binary files | FileChannel | Use direct buffers (64KB) |
| Sequential access | BufferedReader/Writer | 8KB-64KB buffer size |
| Random access | Memory-mapped | Best 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