File I/O with java.nio.file
Programs forget everything when they stop, unless they save it. Files let your program keep data between runs, read reports or CSV exports from other systems, and write logs and receipts. Java's modern file API lives in the package java.nio.file, built around two classes: Path, which names a file or folder, and Files, which does the work.
In this lesson you will write and read text files, process a CSV file line by line, build paths that work on every operating system, and copy, move, list and delete files. Every file method can fail (the disk is full, the file is missing), so these methods throw the checked IOException you met in Exceptions.
Writing and reading text files
For small files, four methods cover almost everything: Files.writeString and Files.readString handle the whole file as one string, while Files.write and Files.readAllLines work with a list of lines.
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.List;
public class Diary {
public static void main(String[] args) throws IOException {
Path file = Path.of("notes.txt");
Files.writeString(file, "Buy milk\nPay electricity bill\n");
Files.writeString(file, "Call Meera\n", StandardOpenOption.APPEND);
String all = Files.readString(file);
System.out.print(all);
List<String> lines = Files.readAllLines(file);
System.out.println("Tasks: " + lines.size() + ", first: " + lines.get(0));
Files.write(file, List.of("Fresh list", "Only this")); // overwrites
System.out.println(Files.readAllLines(file));
System.out.println("Size in bytes: " + Files.size(file));
System.out.println("Exists? " + Files.exists(file));
}
}Buy milk Pay electricity bill Call Meera Tasks: 3, first: Buy milk [Fresh list, Only this] Size in bytes: 23 Exists? true
By default, writing creates the file if needed and replaces its contents. StandardOpenOption.APPEND adds to the end instead. main declares throws IOException to keep the example short; in a real program you would catch the exception and show a helpful message. (The size is 23 bytes on Windows, where Files.write ends lines with \r\n, and 21 on Linux and macOS, which use \n.)
All these methods use UTF-8, so Hindi text and the ₹ sign are stored correctly. Since Java 18, UTF-8 is also the default for the older file classes.
Building paths
A Path is just a name for a location; creating one does not touch the disk. Build paths from parts with Path.of("a", "b") or resolve, and Java uses the right separator for the operating system: \ on Windows and / on Linux and macOS.
import java.nio.file.Path;
public class Paths {
public static void main(String[] args) {
Path base = Path.of("shop", "2026", "october");
Path invoice = base.resolve("invoice-101.txt");
System.out.println(invoice);
System.out.println(invoice.getFileName());
System.out.println(invoice.getParent());
System.out.println(invoice.getNameCount());
System.out.println(invoice.isAbsolute());
System.out.println(Path.of("shop/./2026/../2026/october").normalize());
}
}Output on Windows (on Linux and macOS you see / instead of \):
shop\2026\october\invoice-101.txt invoice-101.txt shop\2026\october 4 false shop\2026\october
These are relative paths: they start from the folder where you ran the program (the working directory). toAbsolutePath() shows the full path, and normalize() removes . and .. parts. Never build paths by gluing strings with "\\"; your program would break on Linux servers.
Processing a CSV file line by line
readAllLines loads the whole file into memory. For large files, such as a log with millions of lines, use Files.lines, which returns a stream that reads lazily, one line at a time. A stream that holds an open file must be closed, so put it in try-with-resources.
Suppose data/marks.csv contains:
name,city,maths,science
Ananya,Lucknow,91,88
Farhan,Pune,78,82
Isha,Kochi,95,90
Rohan,Rohtak,58,64This program reads it, keeps students with an average of 80 or more, and writes them to a new report file:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Stream;
public class Report {
record Result(String name, String city, int maths, int science) {
double average() {
return (maths + science) / 2.0;
}
}
static Result parse(String line) {
String[] p = line.split(",");
return new Result(p[0], p[1], Integer.parseInt(p[2]), Integer.parseInt(p[3]));
}
public static void main(String[] args) throws IOException {
Path input = Path.of("data", "marks.csv");
Path output = Path.of("reports", "toppers.txt");
List<Result> results;
try (Stream<String> lines = Files.lines(input)) {
results = lines.skip(1) // skip the header row
.map(Report::parse)
.toList();
}
List<String> report = results.stream()
.filter(r -> r.average() >= 80)
.map(r -> "%s (%s): %.1f".formatted(r.name(), r.city(), r.average()))
.toList();
Files.createDirectories(output.getParent());
Files.write(output, report);
System.out.println("Read " + results.size() + " students, wrote " + report.size() + " toppers");
Files.readAllLines(output).forEach(System.out::println);
}
}Read 4 students, wrote 3 toppers Ananya (Lucknow): 89.5 Farhan (Pune): 80.0 Isha (Kochi): 92.5
This is a realistic pattern: parse each line into a record, process the records with a stream, and write the result. Files.createDirectories creates the reports folder (and any missing parents) if it does not exist yet, and does nothing if it does. Simple split(",") works for clean data like this; real-world CSV files with quoted commas need a CSV library.
Copying, moving, listing and deleting
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.stream.Stream;
public class Manage {
public static void main(String[] args) throws IOException {
Path reports = Path.of("reports");
Path original = reports.resolve("toppers.txt");
Path backup = reports.resolve("toppers-backup.txt");
Files.copy(original, backup, StandardCopyOption.REPLACE_EXISTING);
Files.move(backup, reports.resolve("old-toppers.txt"), StandardCopyOption.REPLACE_EXISTING);
try (Stream<Path> files = Files.list(reports)) {
files.map(p -> p.getFileName().toString())
.sorted()
.forEach(name -> System.out.println("found " + name));
}
System.out.println("Deleted? " + Files.deleteIfExists(reports.resolve("old-toppers.txt")));
System.out.println("Deleted again? " + Files.deleteIfExists(reports.resolve("old-toppers.txt")));
try {
Files.readString(Path.of("missing.txt"));
} catch (NoSuchFileException e) {
System.out.println("No such file: " + e.getMessage());
}
}
}found old-toppers.txt found toppers.txt Deleted? true Deleted again? false No such file: missing.txt
Without REPLACE_EXISTING, copying or moving onto an existing file throws FileAlreadyExistsException. Files.list shows one folder's entries; Files.walk goes into sub-folders too. Both return streams that must be closed. The order of Files.list is not guaranteed, which is why the example sorts the names. A missing file throws NoSuchFileException, a subclass of IOException.
Common mistakes
- Not closing
Files.lines,Files.listorFiles.walk. Use try-with-resources, or the file stays locked (especially on Windows). - Using
readAllLineson huge files. It loads everything into memory; stream withFiles.linesinstead. - Hard-coding separators. Build paths with
Path.of(...)andresolve, not"folder\\file.txt". - Forgetting that writing replaces the file. Use
StandardOpenOption.APPENDto add. - Confusing the working directory. A relative path starts where the program runs, which in an IDE is usually the project folder.
Key points
Path.of(...)andresolvebuild paths;Filesreads, writes and manages files.Files.writeString/readStringandFiles.write/readAllLineshandle small text files in UTF-8.StandardOpenOption.APPENDadds to a file instead of replacing it.Files.linesstreams large files line by line; close it with try-with-resources.Files.copy,move,deleteIfExists,createDirectories,listandwalkmanage files and folders.- File operations throw
IOException; missing files giveNoSuchFileException.
Exercise
Write Expenses.java that creates expenses.csv with a header date,category,amount and five rows of your own (for example groceries, travel and phone bills in rupees). Then read the file back with Files.lines, skip the header, and write a file summary.txt with one line per category showing its total, sorted by category. Print the summary file.
Show solution
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Expenses {
public static void main(String[] args) throws IOException {
Path csv = Path.of("expenses.csv");
Files.write(csv, List.of(
"date,category,amount",
"2026-10-01,Groceries,1850",
"2026-10-03,Travel,420",
"2026-10-05,Phone,299",
"2026-10-12,Groceries,960",
"2026-10-20,Travel,1150"));
Map<String, Integer> totals;
try (Stream<String> lines = Files.lines(csv)) {
totals = lines.skip(1)
.map(line -> line.split(","))
.collect(Collectors.groupingBy(p -> p[1], TreeMap::new,
Collectors.summingInt(p -> Integer.parseInt(p[2]))));
}
List<String> summary = totals.entrySet().stream()
.map(e -> e.getKey() + ": Rs " + e.getValue())
.toList();
Path out = Path.of("summary.txt");
Files.write(out, summary);
Files.readAllLines(out).forEach(System.out::println);
}
}Groceries: Rs 2810 Phone: Rs 299 Travel: Rs 1570
groupingBy with a TreeMap adds up each category and keeps them sorted. The stream from Files.lines is closed by try-with-resources before the summary is written.
Quick Quiz
1. Which method reads a whole small text file into one String?
readString returns the entire file content; lines returns a lazy stream.
2. How do you add text to the end of an existing file?
Without APPEND, writing replaces the file's contents.
3. Why should Files.lines(path) be used inside try-with-resources?
The stream holds an open file handle, so it must be closed.
4. Which code builds a path that works on both Windows and Linux?
Path.of with separate parts uses the correct separator for each operating system.
Learn Java with a mentor
Free lessons get you started. Our Java, C & C++ Programming training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
