Master Java's NIO.2 API for reading, writing, and managing files and directories using Path, Files, and Streams, with proper resource management and real-world data processing patterns.
File I/O is essential in virtually every backend Java application: reading configuration files, processing data imports, writing logs and reports, managing uploaded files, and exporting data. Java's NIO.2 API (introduced in Java 7, in the java.nio.file package) replaced the older java.io.File class with a cleaner, more powerful design. The Path interface and Files utility class provide a concise, exception-safe API that integrates with Java streams.
Understanding when to use the convenience methods (Files.readString, Files.writeString) versus streaming readers (BufferedReader, Scanner) is critical for memory efficiency — loading a 2 GB CSV file with Files.readString() will crash the JVM, but processing it line-by-line with BufferedReader uses constant memory regardless of file size.
1import java.nio.file.*;
2import java.io.IOException;
3
4// Creating Path objects
5Path absolute = Path.of("/home/user/data/report.csv");
6Path relative = Path.of("reports", "data", "report.csv");
7Path home = Path.of(System.getProperty("user.home"));
8
9// Path operations (no file system access — just string manipulation)
10Path file = Path.of("/home/user/projects/app/data/report.csv");
11System.out.println(file.getFileName()); // report.csv
12System.out.println(file.getParent()); // /home/user/projects/app/data
13System.out.println(file.getRoot()); // /
14System.out.println(file.getNameCount()); // 6
15System.out.println(file.getName(4)); // data
16
17// Resolving paths (concatenation)
18Path base = Path.of("/home/user");
19Path resolved = base.resolve("documents/report.csv");
20System.out.println(resolved); // /home/user/documents/report.csv
21
22Path relative2 = base.relativize(resolved);
23System.out.println(relative2); // documents/report.csv
24
25// Checking file system
26Path config = Path.of("config.properties");
27System.out.println(Files.exists(config)); // true/false
28System.out.println(Files.isReadable(config)); // true/false
29System.out.println(Files.isDirectory(config)); // false
30System.out.println(Files.size(config)); // size in bytes (throws if not found)
31
32// Get basic file attributes
33var attrs = Files.readAttributes(config,
34 java.nio.file.attribute.BasicFileAttributes.class);
35System.out.println(attrs.creationTime());
36System.out.println(attrs.lastModifiedTime());
37System.out.println(attrs.size());1import java.nio.charset.StandardCharsets;
2
3// Small files: convenience methods (loads entire file into memory)
4Path path = Path.of("config.txt");
5
6// Read entire file as String (UTF-8 by default)
7String content = Files.readString(path); // Java 11+
8// or
9String contentLegacy = Files.readString(path, StandardCharsets.UTF_8);
10
11// Write string to file (creates or overwrites)
12Files.writeString(path, "Hello, World!");
13Files.writeString(path, "Hello, World!", StandardCharsets.UTF_8,
14 StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
15
16// Append to existing file
17Files.writeString(path, "\nNew line",
18 StandardOpenOption.CREATE, StandardOpenOption.APPEND);
19
20// Read all lines into a List
21List<String> lines = Files.readAllLines(path);
22lines.forEach(System.out::println);
23
24// Write multiple lines
25Files.write(path, List.of("line1", "line2", "line3"));
26
27// Large files: BufferedReader (constant memory regardless of file size)
28try (BufferedReader reader = Files.newBufferedReader(path)) {
29 String line;
30 int lineNum = 0;
31 while ((line = reader.readLine()) != null) {
32 lineNum++;
33 System.out.printf("%4d: %s%n", lineNum, line);
34 }
35} catch (IOException e) {
36 throw new RuntimeException("Failed to read: " + path, e);
37}
38
39// Stream lines lazily (process without loading all into memory)
40try (var lines2 = Files.lines(path)) { // Stream<String>, auto-closed
41 long wordCount = lines2
42 .flatMap(l -> Arrays.stream(l.split("\\s+")))
43 .filter(w -> !w.isBlank())
44 .count();
45 System.out.println("Words: " + wordCount);
46} catch (IOException e) {
47 throw new RuntimeException(e);
48}
49
50// BufferedWriter for writing large output
51try (BufferedWriter writer = Files.newBufferedWriter(path)) {
52 for (int i = 1; i <= 10; i++) {
53 writer.write("Line " + i);
54 writer.newLine(); // platform-independent newline
55 }
56 writer.flush(); // ensure data is written to OS buffer
57} catch (IOException e) {
58 throw new RuntimeException("Write failed: " + path, e);
59}1// Create directories
2Path dir = Path.of("output", "reports", "2024");
3Files.createDirectories(dir); // creates all parent dirs if missing
4
5// Delete
6Files.deleteIfExists(Path.of("temp.txt")); // no exception if not found
7Files.delete(path); // throws NoSuchFileException if not found
8
9// Copy and move
10Path src = Path.of("original.txt");
11Path dest = Path.of("copy.txt");
12Files.copy(src, dest, StandardCopyOption.REPLACE_EXISTING);
13Files.move(src, dest, StandardCopyOption.REPLACE_EXISTING);
14
15// List directory entries (NOT recursive)
16try (var entries = Files.list(Path.of(".")) // Stream<Path>, close manually!
17) {
18 entries
19 .filter(Files::isRegularFile)
20 .filter(p -> p.toString().endsWith(".java"))
21 .forEach(p -> System.out.println(p.getFileName()));
22} catch (IOException e) {
23 throw new RuntimeException(e);
24}
25
26// Walk directory tree (recursive)
27try (var paths = Files.walk(Path.of("src"))) {
28 long javaCount = paths
29 .filter(p -> p.toString().endsWith(".java"))
30 .peek(p -> System.out.println("Found: " + p))
31 .count();
32 System.out.println("Java files: " + javaCount);
33} catch (IOException e) {
34 throw new RuntimeException(e);
35}
36
37// Find files matching a glob pattern
38try (var matches = Files.find(
39 Path.of("."),
40 Integer.MAX_VALUE,
41 (path2, attrs) -> attrs.isRegularFile()
42 && path2.toString().endsWith(".log")
43)) {
44 matches.forEach(System.out::println);
45} catch (IOException e) {
46 throw new RuntimeException(e);
47}
48
49// Temporary files/dirs (cleaned up when JVM exits)
50Path tempFile = Files.createTempFile("prefix", ".tmp");
51Path tempDir = Files.createTempDirectory("work");
52tempFile.toFile().deleteOnExit();1// Write binary data
2Path binPath = Path.of("data.bin");
3byte[] data = {72, 101, 108, 108, 111}; // 'Hello' in ASCII
4Files.write(binPath, data);
5
6// Read binary data
7byte[] read = Files.readAllBytes(binPath);
8System.out.println(new String(read)); // Hello
9
10// DataOutputStream: write typed values as binary
11try (
12 FileOutputStream fos = new FileOutputStream("record.bin");
13 DataOutputStream dos = new DataOutputStream(fos)
14) {
15 dos.writeUTF("Alice"); // length-prefixed UTF-8 string
16 dos.writeInt(25); // 4 bytes, big-endian
17 dos.writeDouble(3.85); // 8 bytes, IEEE 754
18} catch (IOException e) {
19 throw new RuntimeException(e);
20}
21
22// DataInputStream: read back in same order
23try (
24 FileInputStream fis = new FileInputStream("record.bin");
25 DataInputStream dis = new DataInputStream(fis)
26) {
27 String name = dis.readUTF();
28 int age = dis.readInt();
29 double gpa = dis.readDouble();
30 System.out.printf("%s, age %d, GPA %.2f%n", name, age, gpa);
31} catch (IOException e) {
32 throw new RuntimeException(e);
33}
34
35// RandomAccessFile: seek to arbitrary positions
36try (RandomAccessFile raf = new RandomAccessFile("data.bin", "rw")) {
37 raf.seek(raf.length()); // go to end of file
38 raf.writeBytes("appended");
39 raf.seek(0); // go back to beginning
40 System.out.println(raf.readLine());
41} catch (IOException e) {
42 throw new RuntimeException(e);
43}1import java.util.Properties;
2
3// Reading .properties files
4Properties props = new Properties();
5Path propsPath = Path.of("application.properties");
6
7try (InputStream in = Files.newInputStream(propsPath)) {
8 props.load(in); // parses key=value format
9} catch (IOException e) {
10 throw new RuntimeException("Cannot load config", e);
11}
12
13String host = props.getProperty("db.host", "localhost");
14int port = Integer.parseInt(props.getProperty("db.port", "5432"));
15System.out.printf("Connecting to %s:%d%n", host, port);
16
17// Writing properties
18Properties config = new Properties();
19config.setProperty("app.name", "MyApp");
20config.setProperty("app.version", "1.0.0");
21config.setProperty("max.connections", "10");
22
23try (OutputStream out = Files.newOutputStream(
24 Path.of("config.properties"),
25 StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
26 config.store(out, "Application Configuration");
27} catch (IOException e) {
28 throw new RuntimeException(e);
29}
30
31// Loading from classpath (common in Spring apps)
32Properties appConfig = new Properties();
33try (InputStream in = FileProcessor.class
34 .getResourceAsStream("/application.properties")) {
35 if (in != null) props.load(in);
36} catch (IOException e) {
37 System.err.println("Config not found, using defaults");
38}A complete program that reads a CSV file line-by-line with BufferedReader, processes the data, and writes a formatted report and a summary file, demonstrating proper resource management and error handling.
1import java.io.*;
2import java.nio.file.*;
3import java.util.*;
4import java.util.stream.*;
5
6/**
7 * CsvReportGenerator.java
8 * Reads sales data from CSV, computes stats, writes an HTML and text report.
9 */
10public class CsvReportGenerator {
11
12 record SaleRecord(String region, String product, double amount, int units) {}
13
14 public static void main(String[] args) throws IOException {
15 // Create sample CSV in temp file
16 Path inputPath = createSampleCsv();
17 Path outputDir = Files.createDirectories(Path.of("output"));
18
19 List<SaleRecord> sales = readSalesCsv(inputPath);
20 System.out.println("Loaded " + sales.size() + " sales records");
21
22 writeTextReport(sales, outputDir.resolve("report.txt"));
23 writeHtmlReport(sales, outputDir.resolve("report.html"));
24 writeSummary(sales, outputDir.resolve("summary.csv"));
25
26 System.out.println("Reports written to: " + outputDir.toAbsolutePath());
27 }
28
29 static List<SaleRecord> readSalesCsv(Path path) throws IOException {
30 List<SaleRecord> records = new ArrayList<>();
31 List<String> errors = new ArrayList<>();
32
33 try (BufferedReader reader = Files.newBufferedReader(path)) {
34 String line;
35 int lineNum = 0;
36 while ((line = reader.readLine()) != null) {
37 lineNum++;
38 if (lineNum == 1 || line.isBlank()) continue;
39 try {
40 String[] p = line.split(",");
41 records.add(new SaleRecord(
42 p[0].trim(), p[1].trim(),
43 Double.parseDouble(p[2].trim()),
44 Integer.parseInt(p[3].trim())));
45 } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
46 errors.add("Line " + lineNum + ": " + e.getMessage());
47 }
48 }
49 }
50
51 if (!errors.isEmpty()) {
52 System.err.println("Parse errors:");
53 errors.forEach(e -> System.err.println(" " + e));
54 }
55 return records;
56 }
57
58 static void writeTextReport(List<SaleRecord> sales, Path path) throws IOException {
59 try (BufferedWriter w = Files.newBufferedWriter(path)) {
60 w.write("=== SALES REPORT ==="); w.newLine();
61 w.write(String.format("%-12s %-15s %10s %8s",
62 "Region", "Product", "Amount", "Units")); w.newLine();
63 w.write("-".repeat(48)); w.newLine();
64
65 for (SaleRecord s : sales) {
66 w.write(String.format("%-12s %-15s %10.2f %8d",
67 s.region(), s.product(), s.amount(), s.units()));
68 w.newLine();
69 }
70
71 double total = sales.stream().mapToDouble(SaleRecord::amount).sum();
72 w.write("-".repeat(48)); w.newLine();
73 w.write(String.format("%-28s %10.2f", "TOTAL:", total)); w.newLine();
74 }
75 }
76
77 static void writeHtmlReport(List<SaleRecord> sales, Path path) throws IOException {
78 StringBuilder html = new StringBuilder();
79 html.append("<html><body><table border='1'>");
80 html.append("<tr><th>Region</th><th>Product</th><th>Amount</th><th>Units</th></tr>");
81
82 for (SaleRecord s : sales) {
83 html.append(String.format(
84 "<tr><td>%s</td><td>%s</td><td>%.2f</td><td>%d</td></tr>",
85 escapeHtml(s.region()), escapeHtml(s.product()),
86 s.amount(), s.units()));
87 }
88 html.append("</table></body></html>");
89 Files.writeString(path, html.toString()); // small enough for writeString
90 }
91
92 static void writeSummary(List<SaleRecord> sales, Path path) throws IOException {
93 Map<String, Double> byRegion = sales.stream()
94 .collect(Collectors.groupingBy(SaleRecord::region,
95 Collectors.summingDouble(SaleRecord::amount)));
96
97 List<String> lines = new ArrayList<>();
98 lines.add("Region,TotalAmount");
99 byRegion.entrySet().stream()
100 .sorted(Map.Entry.<String, Double>comparingByValue().reversed())
101 .forEach(e -> lines.add(e.getKey() + "," + String.format("%.2f", e.getValue())));
102
103 Files.write(path, lines); // writes each element as a line
104 }
105
106 static String escapeHtml(String s) {
107 return s.replace("&", "&").replace("<", "<").replace(">", ">");
108 }
109
110 static Path createSampleCsv() throws IOException {
111 Path temp = Files.createTempFile("sales", ".csv");
112 Files.writeString(temp, String.join("\n",
113 "Region,Product,Amount,Units",
114 "East,Widget A,1250.00,50",
115 "West,Widget B,980.50,39",
116 "East,Gadget X,2100.00,21",
117 "North,Widget A,750.25,30",
118 "West,Gadget X,1800.00,18",
119 "South,Widget B,620.00,25"
120 ));
121 temp.toFile().deleteOnExit();
122 return temp;
123 }
124}This program covers the full NIO.2 API in a realistic context: Files.createTempFile() for safe temporary file creation, BufferedReader for line-by-line CSV reading with constant memory overhead, BufferedWriter.newLine() for platform-independent line endings, Files.writeString() for small output that fits in memory, Files.write(path, List) for writing a list of lines in one call, Files.createDirectories() for safe directory creation, and a Collectors.groupingBy() aggregation piped to Files.write() for the summary. The HTML escaping demonstrates that even simple file output requires security awareness.
With file I/O mastered, the next lesson covers the Java Collections Framework — the most heavily used part of the standard library. You will learn List, Set, Map, and Queue with their implementations (ArrayList, LinkedList, HashMap, TreeMap, HashSet, ArrayDeque), understand when to choose each, and use Collections utility methods for sorting, searching, and transforming collections.
Your progress is saved in this browser, on this device only.