1. Overwriting a file
When you work with files, it’s important to understand that “write to a file” is not always the same thing. Sometimes you need to completely overwrite a file (for example, when you save a new report), and sometimes you need to append new information to the end of an existing file (for example, an application event log). In some cases, you just need to read the file’s contents without changing it.
In Java (especially in the modern java.nio.file package), the file mode is defined by a set of special options that you pass to write methods, such as Files.write(). These options let you explicitly indicate whether you want to overwrite the file or add something to the end.
How it works
When you call Files.write(path, data), Java by default creates a new file or overwrites an existing one. All the old content of the file will be destroyed, and only the new data will remain.
This default behavior is a kind of “hard reset” of the file. If the file previously had 1000 lines and you wrote one, everything that was there before will vanish without a trace.
Example: writing lines to a file
import java.nio.file.*;
import java.io.IOException;
import java.util.List;
public class OverwriteFileExample {
public static void main(String[] args) throws IOException {
Path path = Paths.get("myfile.txt");
List<String> lines = List.of("Hello, world!", "This is a new entry in the file.");
// Write lines to the file (old content will be removed)
Files.write(path, lines);
System.out.println("File overwritten successfully!");
}
}
After running this code, the file myfile.txt will contain only the two lines from the lines list. Everything that was there before will disappear (with no “remorse” on Java’s part).
2. Appending: adding data to the end of a file
In some tasks (for example, event journaling, logging, data accumulation) you should not destroy the old content of a file but add new lines to the end. In the old API, this was often done with FileWriter and the append flag = true. In the modern API, it’s simpler and more explicit.
How to do it
All you need is to pass the StandardOpenOption.APPEND option to the Files.write() method:
import java.nio.file.*;
import java.io.IOException;
import java.util.List;
public class AppendFileExample {
public static void main(String[] args) throws IOException {
Path path = Paths.get("myfile.txt");
List<String> moreLines = List.of("Added one more line.", "And another one!");
// Append lines to the end of the file (old content is preserved)
Files.write(path, moreLines, StandardOpenOption.APPEND);
System.out.println("Lines appended to the end of the file!");
}
}
Important note: if the file does not exist, attempting to append will cause an error—Java will not automatically create the file in APPEND mode. To create the file if it’s missing, use both options:
Files.write(path, moreLines, StandardOpenOption.APPEND, StandardOpenOption.CREATE);
What it looks like in practice
- You run the program with write (overwrite) — the file has two lines.
- You run the program with append — new lines are added to these, the old ones remain.
3. Combining options: CREATE, APPEND, TRUNCATE_EXISTING
In Java, you can combine several options for more flexible control over file opening modes:
- StandardOpenOption.CREATE — create the file if it doesn’t exist.
- StandardOpenOption.APPEND — append data to the end.
- StandardOpenOption.TRUNCATE_EXISTING — truncate the file to zero bytes (clear it) if it exists.
- StandardOpenOption.CREATE_NEW — create a new file; if it already exists, throw an error.
Example: create the file if it doesn’t exist and append data to the end
Files.write(path, moreLines, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
Example: create the file or fully clear it and write new data
Files.write(path, lines, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
Table: primary write modes
| Option(s) | Behavior |
|---|---|
| (default) | Create file or overwrite existing |
|
Append to end; error if the file doesn’t exist |
|
Append to end; create the file if it doesn’t exist |
|
Truncate the file to zero and write new data |
|
Create a new file; error if the file already exists |
4. Reading and writing binary files
Until now, we’ve worked with strings. But sometimes you need to write or read raw bytes (for example, images, archives, PDF files). In this case, use:
- Files.readAllBytes(path) — reads a file into a byte array.
- Files.write(path, byteArray) — writes a byte array to a file.
Example: copying a file
import java.nio.file.*;
import java.io.IOException;
public class CopyBinaryFileExample {
public static void main(String[] args) throws IOException {
Path source = Paths.get("logo.png");
Path target = Paths.get("logo_copy.png");
byte[] data = Files.readAllBytes(source);
Files.write(target, data);
System.out.println("File copied!");
}
}
Example: appending binary data
byte[] moreData = new byte[] {1, 2, 3, 4, 5};
Files.write(path, moreData, StandardOpenOption.APPEND, StandardOpenOption.CREATE);
Caution: appending binary data to a file that already contains structured data (for example, an image) will usually corrupt it. Use append only for text files or specially prepared binary files (for example, logs).
5. When you need streams (FileInputStream, BufferedReader, etc.)
The methods Files.readAllBytes() and Files.write() are convenient for small and medium-sized files, when you can safely load the entire content into memory at once. If the file is large (for example, gigabytes), or if you want to read it line by line (for example, to analyze logs), use streams.
Example: reading a file line by line with BufferedReader
import java.nio.file.*;
import java.io.*;
public class ReadLinesExample {
public static void main(String[] args) throws IOException {
Path path = Paths.get("myfile.txt");
try (BufferedReader reader = Files.newBufferedReader(path)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println("Line: " + line);
}
}
}
}
Example: writing a file line by line with BufferedWriter and append
import java.nio.file.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
public class WriteAppendExample {
public static void main(String[] args) throws IOException {
Path path = Paths.get("myfile.txt");
try (BufferedWriter writer = Files.newBufferedWriter(
path,
StandardCharsets.UTF_8,
StandardOpenOption.CREATE,
StandardOpenOption.APPEND)) {
writer.write("One more line via BufferedWriter!");
writer.newLine();
}
}
}
6. Practice: create a file and append lines to it
Let’s tie everything together into a single app. Suppose we have a program that keeps a to-do list in a text file. Each time the user adds a new task, we append it to the end of the file.
import java.nio.file.*;
import java.io.IOException;
import java.util.List;
import java.util.Scanner;
public class TodoList {
public static void main(String[] args) throws IOException {
Path path = Paths.get("todo.txt");
Scanner scanner = new Scanner(System.in);
System.out.print("Enter a new task: ");
String task = scanner.nextLine();
// Append the task to the end of the file (create the file if it doesn't exist)
Files.write(path,
List.of(task),
StandardOpenOption.CREATE,
StandardOpenOption.APPEND);
System.out.println("Task added!");
}
}
Run the program several times — each task will appear on a new line in the file. That’s our first “persistent” to-do list!
7. Error handling: what can go wrong?
Working with files is always associated with the risk of errors. Here’s what can happen:
- IOException — the base exception for all input/output errors. It can occur if the file is locked by another program, if there are no read/write permissions, if the disk is full, etc.
- NoSuchFileException — if you try to read or append to a file that doesn’t exist (and you didn’t specify the CREATE option).
- FileAlreadyExistsException — if you use the CREATE_NEW option and the file already exists.
Recommendation: always wrap file operations in a try-catch:
try {
// your file operations
} catch (IOException e) {
System.out.println("Error while working with the file: " + e.getMessage());
}
8. Common mistakes when working with file opening modes
Mistake #1: forgot to specify the CREATE option when appending. If you try to append (APPEND) to a file that doesn’t exist yet, you’ll get a NoSuchFileException. Always add CREATE if you want the file to be created automatically.
Mistake #2: accidental overwrite. Calling Files.write(path, data) without additional options destroys all the file’s old content. If you want to add data rather than destroy the old content, use APPEND.
Mistake #3: trying to append binary data to a text file (or vice versa). If you mix text and binary data in one file, you’ll likely get an unreadable file. Always stick to a single format per file.
Mistake #4: forgot to close the stream. If you use streams (BufferedWriter, BufferedReader), don’t forget to close them (preferably via try-with-resources), otherwise the file may remain locked.
Mistake #5: didn’t handle exceptions. Any file operation can throw an IOException. If you don’t handle the error, the program will crash.
GO TO FULL VERSION