1. Reading an entire file
Reading all bytes
Sometimes you need to pull all the contents out of a file at once—as is, without parsing—be it an image, an archive, or some binary format. For that, Java has the method Files.readAllBytes(path). It returns a byte array (byte[]), i.e., the raw file data that you can use however you like.
import java.nio.file.*;
public class ReadBytesExample {
public static void main(String[] args) throws Exception {
Path path = Paths.get("example.txt");
byte[] allBytes = Files.readAllBytes(path);
System.out.println("File length in bytes: " + allBytes.length);
}
}
Note: If the file is large (gigabytes), this approach may lead to memory issues—because the entire file is loaded into RAM at once.
Reading all lines
For text files there’s a more human-friendly way: Files.readAllLines(path). It returns a list of lines (List<String>), where each line is one line from the file (line separators are handled automatically).
import java.nio.file.*;
import java.util.List;
public class ReadLinesExample {
public static void main(String[] args) throws Exception {
Path path = Paths.get("example.txt");
List<String> lines = Files.readAllLines(path);
for (String line : lines) {
System.out.println(line);
}
}
}
Important: By default, the system charset is used. If you want to specify the charset explicitly (for example, UTF-8), use the overloaded method:
List<String> lines = Files.readAllLines(path, java.nio.charset.StandardCharsets.UTF_8);
Example: read a file and print it
Let’s add this to our mini app (for example, “notepad” or a “to-do list”):
import java.nio.file.*;
import java.util.List;
public class TodoReader {
public static void main(String[] args) throws Exception {
Path todoPath = Paths.get("todo.txt");
if (Files.exists(todoPath)) {
List<String> tasks = Files.readAllLines(todoPath);
System.out.println("Your tasks for today:");
for (String task : tasks) {
System.out.println("- " + task);
}
} else {
System.out.println("File todo.txt not found. Create a to-do list!");
}
}
}
2. Writing to a file
Writing a byte array
If you have a byte array (for example, the result of serialization, an image, audio), use Files.write(path, bytes). This method creates the file if it doesn’t exist, or overwrites an existing one.
import java.nio.file.*;
public class WriteBytesExample {
public static void main(String[] args) throws Exception {
Path path = Paths.get("output.bin");
byte[] data = {1, 2, 3, 4, 5};
Files.write(path, data);
System.out.println("Bytes written to output.bin");
}
}
Writing a list of lines
For text files, it’s often more convenient to use a list of lines—each line will be written separately with a line break.
import java.nio.file.*;
import java.util.Arrays;
import java.util.List;
public class WriteLinesExample {
public static void main(String[] args) throws Exception {
Path path = Paths.get("todo.txt");
List<String> tasks = Arrays.asList(
"Buy milk",
"Call grandma",
"Do Java homework"
);
Files.write(path, tasks);
System.out.println("To-do list written to todo.txt");
}
}
Warning: By default, the file will be overwritten! If it already existed, the old tasks will be lost. (How not to lose the old content—we’ll cover that a bit later.)
Writing a string to a file
If you want to write a single string, just create a list with one element or use Files.write(path, string.getBytes()):
import java.nio.file.*;
public class WriteStringExample {
public static void main(String[] args) throws Exception {
Path path = Paths.get("hello.txt");
String message = "Hello, Java!";
Files.write(path, message.getBytes());
System.out.println("String written to hello.txt");
}
}
Specifying the charset explicitly
To avoid garbled characters when working with Russian (and other non-ASCII) text, always specify the charset:
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.List;
public class WriteLinesUtf8Example {
public static void main(String[] args) throws Exception {
Path path = Paths.get("hello.txt");
List<String> lines = List.of("Hello, world!", "This is Java.");
Files.write(path, lines, StandardCharsets.UTF_8);
}
}
3. Error handling
When working with files, Java may throw an IOException. It’s a general signal that “something went wrong” at the I/O level. There are many possible reasons: the file doesn’t exist, insufficient permissions, the disk is full or busy, or someone even yanked out a flash drive at the worst moment.
Therefore, file read/write methods always require us to be prepared to handle such situations. You do this with try-catch:
import java.nio.file.*;
import java.util.List;
import java.io.IOException;
public class SafeReadExample {
public static void main(String[] args) {
Path path = Paths.get("todo.txt");
try {
List<String> lines = Files.readAllLines(path);
System.out.println("File contents:");
for (String line : lines) {
System.out.println(line);
}
} catch (IOException ex) {
System.out.println("Error reading file: " + ex.getMessage());
}
}
}
Why bother?
try-catch turns a potential program crash into a controlled situation. If the file doesn’t exist—we calmly inform the user. If the system denies access—we point out the problem. And if the disk disappears in the middle of an operation, the program won’t crash with a scary stack trace but at least warns with a normal message. In other words, IOException is not an enemy, it’s just Java’s way to say: “hey, something outside went wrong, figure out what to do next”.
4. Practical examples
Read a file and print its contents
import java.nio.file.*;
import java.util.List;
import java.io.IOException;
public class PrintFileExample {
public static void main(String[] args) {
Path path = Paths.get("notes.txt");
try {
if (!Files.exists(path)) {
System.out.println("File notes.txt not found.");
return;
}
List<String> lines = Files.readAllLines(path, java.nio.charset.StandardCharsets.UTF_8);
System.out.println("Contents of notes.txt:");
for (String line : lines) {
System.out.println(line);
}
} catch (IOException e) {
System.out.println("Error reading file: " + e.getMessage());
}
}
}
Write a string to a new file
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.io.IOException;
public class WriteFileExample {
public static void main(String[] args) {
Path path = Paths.get("greeting.txt");
String content = "Welcome to the world of Java IO!";
try {
Files.write(path, content.getBytes(StandardCharsets.UTF_8));
System.out.println("String written to greeting.txt");
} catch (IOException e) {
System.out.println("Error writing file: " + e.getMessage());
}
}
}
Write a list of lines to a file
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.io.IOException;
public class WriteListExample {
public static void main(String[] args) {
Path path = Paths.get("shopping.txt");
List<String> items = List.of("Bread", "Milk", "Cheese");
try {
Files.write(path, items, StandardCharsets.UTF_8);
System.out.println("Shopping list written to shopping.txt");
} catch (IOException e) {
System.out.println("Error writing file: " + e.getMessage());
}
}
}
5. A quick note on streams
The methods Files.readAllBytes, Files.readAllLines, Files.write are “all at once”: either the entire file into memory, or all content straight to disk. That’s convenient for small files, but not for large ones (you can get OutOfMemoryError or just “hang” while reading gigabyte-sized logs).
For working with large files or reading line by line use streams:
- For text: BufferedReader, BufferedWriter
- For bytes: InputStream, OutputStream
We’ll talk more about streams in the next lectures, but here’s a small teaser:
import java.nio.file.*;
import java.io.*;
public class BufferedReaderExample {
public static void main(String[] args) {
Path path = Paths.get("bigfile.txt");
try (BufferedReader reader = Files.newBufferedReader(path)) {
String line;
while ((line = reader.readLine()) != null) {
// Process the line
System.out.println(line);
}
} catch (IOException e) {
System.out.println("Error reading file: " + e.getMessage());
}
}
}
6. Useful tips
- Overwriting a file: All Files.write methods overwrite the file by default. If you need to append to the end, use additional options (more on that in the next lecture).
- Charset: Always specify the charset explicitly when working with non-ASCII text. This will save you from garbled output. A good choice is StandardCharsets.UTF_8.
- Paths: Use Paths.get(...) for cross-platform code. Never hardcode slashes (/ or \) manually.
- File names: Do not use forbidden characters in a file name (?, *, :, <, >, |, etc.), especially if your code must run on Windows.
7. Common mistakes when reading and writing files
Error #1: Not handling IOException. Very often beginners call Files.readAllLines(path) without try-catch, and at the first problem (file not found, no permissions, corrupted disk) the program crashes. Always handle exceptions!
Error #2: Using readAllBytes/readAllLines with large files. If a file is hundreds of megabytes or gigabytes, trying to load it entirely can “kill” your program. For such cases, use streams (BufferedReader).
Error #3: Not specifying the charset when writing/reading text. If you don’t specify a charset, results may differ on different computers and OSes. The problem shows up especially often with Cyrillic. Use StandardCharsets.UTF_8 or the charset you need explicitly.
Error #4: Expecting that File/Path is the file itself. The File or Path class is just a “pointer” to a file, not the file itself. Creating an object does not create a file on disk. To create a file, use methods like Files.createFile, Files.write, and so on.
Error #5: Not closing the stream (if you use streams). If you use streams manually (for example, BufferedReader), be sure to close them (preferably with try-with-resources). Otherwise, the file may remain “locked” and unavailable to other programs.
GO TO FULL VERSION