- Understand the concept of streams in Java I/O
- Learn the difference between byte and character streams
- Master binary file operations
- Work with directories and file listings
- Understand Java NIO (New I/O) basics and Path API
Working with Files and Streams
Beyond basic file reading and writing, Java offers streams for more sophisticated file operations and the NIO API for better performance.
Understanding Streams
In Java, streams are sequences of data. Two main categories:
Byte Streams (for binary data)
- InputStream and OutputStream
- Work with raw bytes (8-bit)
- Used for images, audio, video, executables
Character Streams (for text data)
- Reader and Writer
- Work with characters (16-bit Unicode)
- Handle character encoding automatically
- Used for text files
Binary File Operations
Reading Binary Files
import java.io.*;
public void copyBinaryFile(String source, String destination) {
try (FileInputStream in = new FileInputStream(source);
FileOutputStream out = new FileOutputStream(destination)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
System.out.println("File copied successfully!");
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
}
}
Working with Data Streams
// Writing primitive types to binary file
try (DataOutputStream out = new DataOutputStream(
new FileOutputStream("data.bin"))) {
out.writeInt(42);
out.writeDouble(3.14);
out.writeUTF("Hello, World!");
} catch (IOException e) {
e.printStackTrace();
}
// Reading primitive types from binary file
try (DataInputStream in = new DataInputStream(
new FileInputStream("data.bin"))) {
int number = in.readInt();
double pi = in.readDouble();
String text = in.readUTF();
System.out.println(number + ", " + pi + ", " + text);
} catch (IOException e) {
e.printStackTrace();
}
Working with Directories
Listing Directory Contents
File dir = new File("myFolder");
File[] files = dir.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
System.out.println("[DIR] " + file.getName());
} else {
System.out.println("[FILE] " + file.getName() +
" (" + file.length() + " bytes)");
}
}
}
Creating Directory Trees
File dir = new File("parent/child/grandchild");
if (dir.mkdirs()) {
System.out.println("Directory tree created");
}
Java NIO (New I/O)
Java 7 introduced the NIO.2 API with the Path and Files classes, providing more powerful file operations.
Path API
import java.nio.file.*;
// Creating paths
Path path1 = Paths.get("data.txt");
Path path2 = Paths.get("/home/user/documents");
Path path3 = path2.resolve("file.txt"); // Combine paths
// Path information
System.out.println("File name: " + path1.getFileName());
System.out.println("Parent: " + path1.getParent());
System.out.println("Absolute: " + path1.toAbsolutePath());
Files Utility Class
import java.nio.file.*;
import java.io.IOException;
import java.util.List;
import java.util.Arrays;
// Reading all lines at once
try {
List<String> lines = Files.readAllLines(Paths.get("data.txt"));
for (String line : lines) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
// Writing all lines at once
List<String> lines = Arrays.asList("Line 1", "Line 2", "Line 3");
try {
Files.write(Paths.get("output.txt"), lines);
} catch (IOException e) {
e.printStackTrace();
}
Advanced File Operations with NIO
// Copy file
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
// Move file
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
// Delete file
Files.delete(path);
// Check existence
if (Files.exists(path)) {
System.out.println("File exists");
}
// Get file attributes
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
System.out.println("Size: " + attrs.size());
System.out.println("Created: " + attrs.creationTime());
System.out.println("Modified: " + attrs.lastModifiedTime());
Walking Directory Trees
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.io.IOException;
// List all files recursively
try (Stream<Path> paths = Files.walk(Paths.get("myFolder"))) {
paths.filter(Files::isRegularFile)
.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
// Custom directory visitor
Files.walkFileTree(Paths.get("myFolder"), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
System.out.println("File: " + file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
System.out.println("Directory: " + dir);
return FileVisitResult.CONTINUE;
}
});
Real-World Example: Log File Analyzer
import java.nio.file.*;
import java.io.IOException;
import java.util.stream.Stream;
public class LogAnalyzer {
public void analyzeLogFile(String logFile) {
try (Stream<String> lines = Files.lines(Paths.get(logFile))) {
long errorCount = lines.filter(line -> line.contains("ERROR"))
.count();
System.out.println("Total errors: " + errorCount);
} catch (IOException e) {
System.out.println("Error reading log: " + e.getMessage());
}
}
public void findLargeFiles(String directory, long minSize) {
try (Stream<Path> paths = Files.walk(Paths.get(directory))) {
paths.filter(Files::isRegularFile)
.filter(p -> {
try {
return Files.size(p) > minSize;
} catch (IOException e) {
return false;
}
})
.forEach(p -> {
try {
System.out.println(p + " (" + Files.size(p) + " bytes)");
} catch (IOException e) {
e.printStackTrace();
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
Performance Tips
- Use buffered streams for better performance
- Prefer NIO for bulk operations (copy, move, delete)
- Use try-with-resources to prevent resource leaks
- Process large files line by line instead of loading all at once
- Use appropriate stream types (byte vs character)
Key Takeaways
- Byte streams for binary data, character streams for text
- InputStream/OutputStream hierarchy for byte operations
- Reader/Writer hierarchy for character operations
- Java NIO provides modern, powerful file operations
- Path and Files classes simplify common file tasks
- Stream API integration makes file processing elegant
- Always handle IOException appropriately
Congratulations!
You've completed the Exception Handling and File I/O chapter! You now know:
- How to handle exceptions gracefully
- How to read and write text files
- How to work with binary files
- How to use Java NIO for advanced operations
- Best practices for file I/O
These skills are essential for real-world Java development!
