1. Introduction
Let’s be honest right away: if you’ve ever seen something like Привет instead of Russian text, you’ve already fallen victim to an incorrect encoding. This happens when a file was written in one encoding and read in another. For example, the file was saved as UTF-8 but read as Windows-1251, or vice versa.
By default, Java uses the system encoding, which you can get like this:
System.out.println(System.getProperty("file.encoding"));
On one machine it might be UTF-8, on another — Windows-1251, and elsewhere — ISO-8859-1. That’s why it’s always better to specify the encoding explicitly. This is especially important if you work with multilingual data; if files will be used on different machines or opened in other programs; or if you need your code to behave consistently in any environment, not just on your own machine.
The Charset class: your friend in the world of encodings
In Java, the class java.nio.charset.Charset is used to work with encodings. It lets you specify an encoding by name (for example, "UTF-8") or use standard constants (StandardCharsets.UTF_8).
Examples of standard encodings:
| Encoding | Java constant |
|---|---|
| UTF-8 | |
| UTF-16 | |
| ISO-8859-1 | |
| Windows-1251 | |
Using constants is preferable: there’s less chance of misspelling a name, and you won’t get an UnsupportedCharsetException.
2. Reading files with an explicit encoding
Old way:
import java.io.*;
import java.nio.charset.StandardCharsets;
BufferedReader reader = new BufferedReader(
new InputStreamReader(
new FileInputStream("example.txt"),
StandardCharsets.UTF_8 // <-- Specify the encoding explicitly
)
);
Modern way:
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.io.BufferedReader;
BufferedReader reader = Files.newBufferedReader(
Paths.get("example.txt"),
StandardCharsets.UTF_8 // <-- Specify the encoding explicitly
);
It’s recommended to use the second approach — it’s shorter, safer, and works well with try-with-resources.
Example: read a line from a file
try (BufferedReader reader = Files.newBufferedReader(
Paths.get("hello.txt"),
StandardCharsets.UTF_8)) {
String line = reader.readLine();
System.out.println("Read: " + line);
}
Why this matters: If the file was saved in UTF-8 but you read it as Windows-1251, Cyrillic characters will be garbled. If you specify the correct encoding, the text will be read correctly on any OS.
3. Writing files with an explicit encoding
Old way:
import java.io.*;
import java.nio.charset.StandardCharsets;
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream("example.txt"),
StandardCharsets.UTF_8 // <-- Specify the encoding explicitly
)
);
Modern way:
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.io.BufferedWriter;
BufferedWriter writer = Files.newBufferedWriter(
Paths.get("example.txt"),
StandardCharsets.UTF_8 // <-- Specify the encoding explicitly
);
Example: write a line to a file
try (BufferedWriter writer = Files.newBufferedWriter(
Paths.get("hello.txt"),
StandardCharsets.UTF_8)) {
writer.write("Hello, world!");
}
Result: The file will be saved in UTF-8, and it can be opened correctly in any editor that supports UTF-8.
4. Useful details
How to list supported encodings
import java.nio.charset.Charset;
public class ListCharsets {
public static void main(String[] args) {
System.out.println("Available encodings:");
Charset.availableCharsets().forEach((name, charset) -> System.out.println(name));
}
}
Tip: If you use an exotic encoding (for example, for ancient Chinese ideographs or Martian emoji), check whether your JVM supports it.
Using try-with-resources: don’t forget to close streams
When working with files, it’s important to close streams to avoid resource leaks. Modern Java code uses the try-with-resources construct:
try (BufferedReader reader = Files.newBufferedReader(path, charset)) {
// Work with the file
}
The stream will close automatically, even if an error occurs.
Recommendations
- It’s better to always specify the encoding explicitly when reading and writing files, even if you’re sure that “the default works.”
- Use UTF-8 for new files — it’s the de facto standard, especially if you work with the web, JSON, XML, or want your files to be readable everywhere.
- For legacy files (for example, an export from 1C, old databases, CSV from Windows) use the encoding they were saved with (for example, Windows-1251, ISO-8859-1).
- Don’t use outdated classes where the encoding isn’t specified explicitly: FileReader/FileWriter. Use InputStreamReader/OutputStreamWriter with an explicit encoding or methods from Files instead.
- For large files use buffering (BufferedReader/BufferedWriter) to avoid consuming too much memory.
5. Common mistakes when working with encodings
Mistake #1: No encoding specified when reading/writing a file.
If you don’t specify an encoding, Java uses the system default ("file.encoding"). It may work on your machine, but your colleague gets “mojibake.”
Mistake #2: Mismatch between encodings when reading and writing.
The file was written in one encoding but read in another. For example, the file was written in UTF-8 but read as Windows-1251 — Cyrillic gets mangled.
Mistake #3: Using the legacy classes FileReader/FileWriter.
These classes don’t let you specify the encoding explicitly — avoid using them. Instead, use InputStreamReader/OutputStreamWriter with an explicit encoding or methods from Files.
Mistake #4: Typo in the encoding name.
For example, you wrote "utf8" instead of "UTF-8" or "win1251" instead of "Windows-1251". Java will throw an UnsupportedCharsetException.
Mistake #5: Stream not closed — the file wasn’t written.
If you don’t use try-with-resources or explicitly close the stream, some data may not be flushed to disk.
GO TO FULL VERSION