“Where did today's orders go?”
Riya's canteen app now writes each order to orders.csv. At night the owner opens the file and finds only the last order. Riya also can't find the file on her laptop: it isn't in the folder where her code is.
Sir, I write every order, but only the last one stays!
Writing a file normally replaces it. To add at the end you must ask for append. And the file is where your program ran from, the working directory, not where your code sits.
Is there an easy way, without all those stream classes?
Yes: Path to name the file, and Files to do the work in one line each. Let's see what each line does on disk.
What are Path and Files?
java.nio.file.Path is an object that represents the location of a file or directory in the file system as a sequence of names; it may be absolute (from the root) or relative (resolved against the current working directory), and creating a Path does not access the disk. java.nio.file.Files is a utility class of static methods that operate on paths: creating, reading, writing, copying, moving and deleting files and directories and querying their attributes. Its methods open and close the required streams internally, use UTF-8 for text by default, and report failures by throwing IOException subclasses such as NoSuchFileException and FileAlreadyExistsException. The older java.io.File class serves a similar purpose.
A Path is an address written on an envelope. Files is the postman who actually goes there to drop off, pick up, copy or remove things.
Words you'll meet in this lesson
/home/riya/app/data/x.csv or E:\app\x.csv.data/x.csv.System.getProperty("user.dir").dir.resolve("a.txt") → dir/a.txt.Old and new file APIs
java.io.File (old) | Path + Files (Java 7+) | |
|---|---|---|
| Name a file | new File("a.txt") | Path.of("a.txt") |
| Read whole text | open a reader and loop | Files.readString(p) |
| Errors | often just false (e.g. delete()) | specific exceptions with reasons |
| Copy / move | not built in | Files.copy, Files.move |
| Default charset | platform default (UTF-8 since Java 18) | UTF-8 |
| Convert | file.toPath() | path.toFile() |
Exam tip: “Files.writeString/write create the file if needed and truncate it by default; pass StandardOpenOption.APPEND to add. Reading a missing file throws NoSuchFileException (NIO) or FileNotFoundException (java.io), both subclasses of IOException.”
Your file toolkit
Path p = Path.of("data", "orders.csv"); // just a name
Files.exists(p) Files.size(p) Files.isDirectory(p)
Files.writeString(p, text) // create or replace
Files.writeString(p, text, StandardOpenOption.APPEND)
Files.write(p, listOfLines)
String all = Files.readString(p);
List<String> lines = Files.readAllLines(p);
Files.createDirectories(p.getParent());
Files.copy(a, b) Files.move(a, b) Files.delete(p) Files.deleteIfExists(p)Small text files
readString, writeString, readAllLines, write(lines): one line each.
Folders
createDirectories, list, isDirectory, walk for whole trees.
Copy, move, delete
copy, move, delete, deleteIfExists, with explicit overwrite options.
Scanner & PrintWriter
Read tokens and numbers from a file; write with println/printf.
The older java.io.File
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
File f = new File("legacy.txt");
try (FileWriter w = new FileWriter(f)) {
w.write("written with java.io.File");
}
System.out.println(f.getName() + " exists=" + f.exists() + " size=" + f.length());
System.out.println("deleted: " + f.delete() + ", deleted again: " + f.delete());
}
}Don't trust relative paths in servers and IDEs. “data/orders.csv” goes wherever the program was started: the project root in an IDE, some other folder on a server. For important files, build the path from a configured base folder: Path.of(config.dataDir(), "orders.csv").
What happens when you call Files.writeString
Resolve the path
The relative path is joined to the working directory, e.g. /home/riya/app/data/orders.csv.Files.writeString(Path.of("data", "orders.csv"), "101,dosa,80\n");Open with options
With no options it opens with CREATE + TRUNCATE_EXISTING + WRITE: create the file if missing, empty it if present. If the folderdatadoesn't exist, you getNoSuchFileException, so create folders first.Encode and write
The text is encoded as UTF-8 and written through a byte stream, as in Lesson 13.1.Close
The method closes the stream before returning, so the data is handed to the OS and the file handle is released. There is nothing for you to forget.Or append
APPEND moves the write position to the end; existing bytes are kept. (Add CREATE too if the file might not exist yet.)Files.writeString(p, "102,idli,40\n", StandardOpenOption.APPEND);
From Path to file and back: both views
Left: the folder and file as you'd see them in a file manager. Right: the Path and List objects on the heap, and the file as the operating system holds it.
Path.of("data", "orders.csv")builds a path: the file's name and location. Nothing on disk changes.Files.createDirectoriesmakes the folder (and any missing parents). No error if it already exists.Files.writeStringcreates the file, writes the text in UTF-8 and closes it, all in one call. An existing file would be replaced.- With
StandardOpenOption.APPEND, the new text goes at the end instead. Files.readAllLinesreads the whole file and returns aList<String>, one element per line, without the line breaks.Files.deleteremoves the file. YourPathobject still exists: it is only a name.
- The Path is a small object on the heap holding a relative path. Relative paths are resolved against the program's working directory, here /home/riya/app.
- The OS creates the
datafolder. The JVM itself stores nothing about it. writeStringopens a stream, encodes the text as UTF-8, writes 12 bytes and closes the stream: the stream code from Lesson 13.1, already written for you.- Appending adds 12 more bytes; the file is now 24 bytes long.
readAllLinesreads all bytes, decodes them and splits on line breaks. Each line becomes a String in a new ArrayList. For a huge file this would fill memory, so you'd read it line by line instead (Lesson 13.3).- After
delete, the OS no longer has the file. The Path object is unchanged;Files.exists(p)would now return false.
Files in real systems
Even apps backed by databases read and write plenty of files: imports, exports, configs, logs and archives.
Watch it with an example: a canteen's daily sales file
Every order is appended to one CSV file per day. At closing time the owner's report reads the file and adds up the sales.
- At 9:10 a dosa is sold. The app appends one line,
time,item,amount, to today's file. - Each new order adds one line at the end. Earlier lines are never rewritten, so a crash can't lose the morning's orders.
- By evening the file has three lines. Opening it in a spreadsheet already works: CSV is just text.
- At closing, the report reads all lines and takes the third field of each, the amount.
- It prints the day's total. One file per day keeps files small and easy to archive. (Real shops would also keep this in a database.)
1. Imports and exports
Banks import salary CSVs from companies; shops export GST and sales reports for accountants.
2. Configuration
Apps read settings (database URL, tax rates, feature flags) from files at startup.
3. Archiving and cleanup
Nightly jobs move old reports into dated folders and delete temporary files older than 30 days.
4. Uploaded documents
KYC PDFs and invoices are saved to a storage folder under a safe generated name, never the name the user sent.
Simplified. Large companies often store such files in cloud object storage, but the read-write-move ideas are the same.
Create, append, read, delete
import java.io.IOException;
import java.nio.file.*;
import java.util.List;
public class Main {
public static void main(String[] args) throws IOException {
Path file = Path.of("data", "orders.csv");
Files.createDirectories(file.getParent());
Files.writeString(file, "101,dosa,80\n");
Files.writeString(file, "102,idli,40\n", StandardOpenOption.APPEND);
List<String> lines = Files.readAllLines(file);
System.out.println(lines.size() + " lines: " + lines);
System.out.println("size " + Files.size(file) + " bytes");
Files.delete(file);
System.out.println("exists after delete: " + Files.exists(file));
}
}Path.of("data", "orders.csv")Builds data/orders.csv with the right separator for the OS.Files.createDirectories(file.getParent())Make sure the folder exists before writing into it.StandardOpenOption.APPENDAdd to the end; without it the file is replaced.Files.delete(file)Throws if the file is missing;deleteIfExistsreturns false instead.
Write, append, read and total
Recorded from the real JVM. The Path and the List of lines appear on the heap; the file itself lives outside the JVM, so watch the notes for what happens on disk.
Worked examples: real problems, solved and traced in memory
Three file tasks from small businesses: saving a list, totalling a CSV, and archiving reports into folders. Each program creates the files it needs, so it runs anywhere.
Save and reload a shopping list
Problem statement
A grocery app saves the customer's list when the app closes and loads it on the next start. Save a List<String> with Files.write, read it back with Files.readAllLines, and show it numbered.
milk, bread, eggs, curd
1. milk 2. bread 3. eggs 4. curd same list? true
Approach
Files.write(path, list)writes each element as one line (UTF-8).Files.readAllLines(path)gives the list back.- Print with a counter.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Total a day's sales from a CSV
Problem statement
Orders are appended to sales.csv as time,item,amount. At closing, read the file, skip the header line, and print the number of orders, the total and the best-selling amount.
header + 4 orders
4 orders, total Rs 335, biggest Rs 120
Approach
- Write the header once, then append each order line.
- Read all lines; start the loop at index 1 to skip the header.
- Split each line on commas and parse the amount.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Archive last month's reports
Problem statement
An office keeps reports in a reports folder. At month end, create archive/2026-09, move every report whose name starts with 2026-09 into it, and list both folders.
reports: 2026-09-gst.txt, 2026-09-sales.txt, 2026-10-sales.txt
reports: [2026-10-sales.txt] archive/2026-09: [2026-09-gst.txt, 2026-09-sales.txt]
Approach
- Create the folders with
Files.createDirectories. - List the reports folder with
Files.list(a stream; close it with try-with-resources). Files.moveeach matching file into the archive folder, keeping its name.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Check yourself
Quick revision
Lesson 13.2 in one screen
Pathnames a file; creating one doesn't touch the disk.Filesdoes the work.- Relative paths resolve against the working directory (
user.dir). writeString/writecreate or replace; addAPPENDto add at the end.readString,readAllLines: whole file into memory, UTF-8 by default.createDirectoriesbefore writing into a new folder.copy/moverefuse to overwrite unlessREPLACE_EXISTING.- Errors:
NoSuchFileException,FileAlreadyExistsException, allIOException.
Interview and viva questions
What is the difference between File and Path?
Both represent file locations. java.io.File is the original API: many methods return booleans without saying why they failed, and copy/move aren't built in. java.nio.file.Path (Java 7) works with the Files utility class, which offers rich operations, specific exceptions, symbolic-link handling and better performance. New code should use Path and Files.
How do you append to a file in Java?
Files.writeString(path, text, StandardOpenOption.CREATE, StandardOpenOption.APPEND), or new FileWriter(file, true) / new FileOutputStream(file, true).
What is the difference between an absolute and a relative path?
An absolute path starts from the file-system root and always points to the same place. A relative path is resolved against the current working directory, so the same relative path can point to different files depending on where the program was started.
When should you not use Files.readAllLines?
For large files, because it loads every line into memory at once. Read line by line with BufferedReader.readLine() or process Files.lines(path) as a stream, which reads lazily.
How do you safely overwrite a file?
Write the new content to a temporary file in the same folder, then Files.move(tmp, target, REPLACE_EXISTING, ATOMIC_MOVE). Readers then see either the old file or the new one, never a half-written file.
Practice problems
Use Path and Files for everyday file work, plus Scanner and PrintWriter. Every program creates its own files, so the output is fixed. The last three are business tasks.
Write and read a string
Write Welcome to MyPhatshala to welcome.txt with Files.writeString and print it back with Files.readString.
(none)
Welcome to MyPhatshala
Show solution and explanation
import java.io.IOException;
import java.nio.file.*;
public class Main {
public static void main(String[] args) throws IOException {
Path p = Path.of("welcome.txt");
Files.writeString(p, "Welcome to MyPhatshala");
System.out.println(Files.readString(p));
}
}How it works: both methods open, use and close the file for you, with UTF-8. Ideal for small text files.
Exists and size
Check whether notes.txt exists before and after creating it with 5 bytes, and print its size.
(none)
before: false after: true, 5 bytes
Show solution and explanation
import java.io.IOException;
import java.nio.file.*;
public class Main {
public static void main(String[] args) throws IOException {
Path p = Path.of("notes.txt");
System.out.println("before: " + Files.exists(p));
Files.writeString(p, "hello");
System.out.println("after: " + Files.exists(p) + ", " + Files.size(p) + " bytes");
}
}How it works: Files.exists asks the OS; Files.size returns the length in bytes. The program creates notes.txt fresh in its own folder, so “before” is false.
Parts of a path
For the path bills/2026/oct/bill-17.pdf, print the file name, the parent folder and the number of name parts.
bills/2026/oct/bill-17.pdf
file: bill-17.pdf parent: bills/2026/oct parts: 4
Show solution and explanation
import java.nio.file.Path;
public class Main {
public static void main(String[] args) {
Path p = Path.of("bills", "2026", "oct", "bill-17.pdf");
System.out.println("file: " + p.getFileName());
System.out.println("parent: " + p.getParent());
System.out.println("parts: " + p.getNameCount());
}
}How it works: a Path is just a name, so this works even though the file doesn't exist. Path.of joins the parts with the right separator for the OS (/ on Linux, \ on Windows).
Count words in a file
Write a two-line review to review.txt, then count its lines and words.
two lines of text
2 lines, 8 words
Show solution and explanation
import java.io.IOException;
import java.nio.file.*;
import java.util.List;
public class Main {
public static void main(String[] args) throws IOException {
Path p = Path.of("review.txt");
Files.writeString(p, "Great dosa and fast service\nWill come again\n");
List<String> lines = Files.readAllLines(p);
int words = 0;
for (String line : lines) {
words += line.trim().split("\\s+").length;
}
System.out.println(lines.size() + " lines, " + words + " words");
}
}How it works: split("\\s+") splits on any run of spaces. This is the basis of text analytics: read lines, split, count.
Sum numbers with Scanner
Write the numbers 120 45 300 15 to amounts.txt, then read them with new Scanner(file) and print the sum.
120 45 300 15
sum = 480
Show solution and explanation
import java.io.*;
import java.nio.file.*;
import java.util.Scanner;
public class Main {
public static void main(String[] args) throws IOException {
Files.writeString(Path.of("amounts.txt"), "120 45\n300 15\n");
int sum = 0;
try (Scanner sc = new Scanner(new File("amounts.txt"))) {
while (sc.hasNextInt()) {
sum += sc.nextInt();
}
}
System.out.println("sum = " + sum);
}
}How it works: a Scanner on a File reads tokens (here ints) across lines, exactly like reading from the keyboard. Close it, as it holds the file open.
Formatted report with PrintWriter
Write a small aligned price table to menu.txt with PrintWriter.printf, then print the file.
dosa 80, thali 120, chai 15
dosa Rs 80 thali Rs 120 chai Rs 15
Show solution and explanation
import java.io.*;
import java.nio.file.*;
public class Main {
public static void main(String[] args) throws IOException {
String[] items = {"dosa", "thali", "chai"};
int[] prices = {80, 120, 15};
try (PrintWriter pw = new PrintWriter("menu.txt")) {
for (int i = 0; i < items.length; i++) {
pw.printf("%-8s Rs %4d%n", items[i], prices[i]);
}
}
System.out.print(Files.readString(Path.of("menu.txt")));
}
}How it works: PrintWriter gives you println and printf on a file. %-8s left-aligns in 8 columns; %4d right-aligns numbers. PrintWriter never throws IOException on write; call checkError() if you need to know.
Attendance from a CSV
attendance.csv has name,status lines (P or A). Count present and absent students and list the absentees.
5 students
present 3, absent 2: [Ravi, Joseph]
Show solution and explanation
import java.io.IOException;
import java.nio.file.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
Path p = Path.of("attendance.csv");
Files.write(p, List.of("Asha,P", "Ravi,A", "Meena,P", "Imran,P", "Joseph,A"));
int present = 0;
List<String> absent = new ArrayList<>();
for (String line : Files.readAllLines(p)) {
String[] f = line.split(",");
if (f[1].equals("P")) present++;
else absent.add(f[0]);
}
System.out.println("present " + present + ", absent " + absent.size() + ": " + absent);
}
}How it works: CSV is the most common data-exchange format in offices. Reading lines and splitting on commas is enough for simple files; real CSVs with quoted commas need a CSV library.
Low-stock alert
inventory.csv has item,qty. Write a new file reorder.txt listing items with quantity below 10, then print it.
rice 40, oil 6, sugar 9, salt 25
oil (only 6 left) sugar (only 9 left)
Show solution and explanation
import java.io.IOException;
import java.nio.file.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
Files.write(Path.of("inventory.csv"), List.of("rice,40", "oil,6", "sugar,9", "salt,25"));
List<String> reorder = new ArrayList<>();
for (String line : Files.readAllLines(Path.of("inventory.csv"))) {
String[] f = line.split(",");
if (Integer.parseInt(f[1]) < 10) reorder.add(f[0] + " (only " + f[1] + " left)");
}
Files.write(Path.of("reorder.txt"), reorder);
System.out.print(Files.readString(Path.of("reorder.txt")));
}
}How it works: read one file, filter, write another: the shape of most batch jobs. The output file can be emailed to the supplier or loaded by another system.
Daily backup that may already exist
Copy ledger.txt to ledger-backup.txt. Copying again fails because the backup exists; catch that, then copy with REPLACE_EXISTING.
ledger.txt
backup exists: ledger-backup.txt backup now 32 bytes, ledger 32 bytes
Show solution and explanation
import java.io.IOException;
import java.nio.file.*;
public class Main {
public static void main(String[] args) throws IOException {
Path ledger = Path.of("ledger.txt"), backup = Path.of("ledger-backup.txt");
Files.writeString(ledger, "opening balance 50000\n");
Files.copy(ledger, backup);
Files.writeString(ledger, "sale 1200\n", StandardOpenOption.APPEND);
try {
Files.copy(ledger, backup);
} catch (FileAlreadyExistsException e) {
System.out.println("backup exists: " + e.getFile());
}
Files.copy(ledger, backup, StandardCopyOption.REPLACE_EXISTING);
System.out.println("backup now " + Files.size(backup) + " bytes, ledger " + Files.size(ledger) + " bytes");
}
}How it works: NIO refuses to overwrite by accident: Files.copy throws FileAlreadyExistsException unless you pass REPLACE_EXISTING. Making overwrites explicit prevents losing data.
Real-world scenario questions (asked in company interviews)
File-handling bugs tend to appear only in production, where paths, permissions and sizes differ from your laptop.
Works in the IDE, NoSuchFileException on the server
A report job reads Path.of("config/tax-rates.csv"). It works in IntelliJ, but on the server it fails with NoSuchFileException although the file is deployed in /opt/app/config. Why?
The interviewer is checking: relative paths and the working directory.
Show a strong answer
- The path is relative; on the server the process starts from a different working directory (e.g. / or the service user's home).
- Log
Path.toAbsolutePath()to confirm, then build paths from a configured base directory (environment variable or config) or load bundled files from the classpath.
Avoid: hard-coding C:\Users\me\... paths.
Import job crashes on month-end files
A transaction import uses Files.readAllLines. It works daily, but the month-end file (8 GB) crashes the job with OutOfMemoryError. How would you change it?
The interviewer is checking: streaming large files.
Show a strong answer
- Read line by line with
Files.newBufferedReaderorFiles.lines(closed in try-with-resources), processing and discarding each line. - Write results in batches; keep only running totals in memory.
- Track the last processed line so a failed run can resume.
Avoid: just raising -Xmx; next month's file will be bigger.
Half-written settings file after a power cut
A POS terminal saves its settings with Files.writeString(settings, json). After a power cut, the file is half-written and the terminal won't start. How do you make saving safe?
The interviewer is checking: atomic replace.
Show a strong answer
- Write to
settings.json.tmpin the same folder, thenFiles.move(tmp, settings, REPLACE_EXISTING, ATOMIC_MOVE). The rename is atomic, so the file is always complete. - Keep the previous version as a backup and validate on startup, falling back if parsing fails.
Avoid: deleting the old file before writing the new one.
A filename that escaped the uploads folder
An upload API saves files with uploads.resolve(userGivenName). A tester uploads a file named ../../app/config.properties and overwrites the app's config. Explain and fix.
The interviewer is checking: path traversal.
Show a strong answer
..segments let the resolved path climb out of the uploads folder: a path traversal attack.- Never use client file names as paths. Generate an ID (e.g. a UUID) for the stored name and keep the original name as metadata.
- If you must use it,
normalize()the result and checktarget.startsWith(uploads); also never pass REPLACE_EXISTING for uploads.
Avoid: only stripping the text “../”; there are many encodings of the same trick.