Java 18 UTF-8 by Default: What Changes and Why
Introduction
Many Java APIs read and write text using the default charset when you do not name one -
OUTPUT of the above code
Explanation of the code
Restoring the old behaviour: file.encoding=COMPAT
For applications which cannot be fixed straight away, starting the JVM with
OUTPUT of the above command
On Windows, the same
How to prepare your code
FileReader, FileWriter, InputStreamReader, Scanner, String.getBytes() and more. Until Java 17, the default charset came from the operating system: UTF-8 on most Linux and macOS systems, but a legacy code page such as Windows-1252 on Windows. So a program which wrote a file with an accented character on Windows could produce a file that the same program misread on Linux. Java 18Click to read tutorial: Overview of New Features in Java 18 (JEP 400) made UTF-8 the default charset on every platform. This tutorial explains UTF-8 by default with examples: what changed, which APIs are affected and which are not, how to find code that depends on the old behaviour, and how to restore it if you must.
Seeing the default charset
The default charset and a file written with it
//DefaultCharsetDemo.java
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
public class DefaultCharsetDemo {
public static void main(String[] args) throws IOException {
System.out.println("Default charset : " + Charset.defaultCharset());
System.out.println("native.encoding : " + System.getProperty("native.encoding"));
Path file = Files.createTempFile("cafe", ".txt");
try (FileWriter writer = new FileWriter(file.toFile())) { // no charset given: uses the default
writer.write("café");
}
System.out.println("Bytes in file : " + Files.size(file));
try (FileReader reader = new FileReader(file.toFile())) {
char[] buffer = new char[10];
int n = reader.read(buffer);
System.out.println("Read back : " + n + " characters, last is U+" + String.format("%04X", (int) buffer[n - 1]));
}
Files.delete(file);
}
}
Default charset : UTF-8 native.encoding : Cp1252 Bytes in file : 5 Read back : 4 characters, last is U+00E9
- The default charset is UTF-8, even though this example ran on Windows. The new system property
native.encoding(added in Java 17) still reports the operating system's own charset - here Windows-1252, reported asCp1252- for code which genuinely needs it. FileWriterwith no charset now writes UTF-8, whereétakes 2 bytes, so the 4-character word is 5 bytes long. Reading it back withFileReader, also UTF-8, gives the same 4 characters - on every operating system.- The text is written as
caféso that the source file itself does not depend on the compiler's encoding. Since Java 18,javacalso reads source files as UTF-8 by default.
-Dfile.encoding=COMPAT makes the default charset the operating system's again, exactly as in Java 17 -
java -Dfile.encoding=COMPAT DefaultCharsetDemo
Default charset : windows-1252 native.encoding : Cp1252 Bytes in file : 4 Read back : 4 characters, last is U+00E9
FileWriter now writes Windows-1252, where é is a single byte - so the file is 4 bytes long, and a program reading it as UTF-8 elsewhere would see an invalid character. This is the inconsistency Java 18 removed. Use COMPAT only as a temporary measure while fixing the code.
What is affected, and what is not
- Affected: every API which uses the default charset when none is given -
FileReaderandFileWriter,InputStreamReaderandOutputStreamWriter,PrintStreamandPrintWritercreated on files or streams,Formatter,Scanner,String(byte[])andString.getBytes(). - Already UTF-8: the NIO
Filesmethods such asFiles.readString()andFiles.newBufferedReader()have always used UTF-8, so they are unaffected. - Not affected:
System.outandSystem.errkeep using the console's encoding, so text printed to a Windows terminal still displays correctly. Their encoding is reported by the propertiesstdout.encodingandstderr.encoding, added in Java 19. - File names are still converted using the operating system's encoding (
sun.jnu.encoding), since they must match what the OS expects.
- Test on Java 17 with UTF-8: run your tests with
-Dfile.encoding=UTF-8on Java 17. If they pass, the switch to Java 18 or later will not affect you. - Name the charset explicitly in code which must read or write a specific encoding, for example
new InputStreamReader(in, StandardCharsets.UTF_8)ornew FileWriter(file, StandardCharsets.ISO_8859_1)- the overloads taking aCharsetwere added in Java 10 and 11. Code which names its charsets does not depend on any default. - Use
native.encodingwhen you truly need the operating system's charset, for example to exchange files with a legacy Windows application. - Check data files written by older versions of your application on Windows, which may be in Windows-1252 and need to be read with that charset explicitly.
native.encoding property, wrote and read a file with the default charset, and saw the old behaviour come back with -Dfile.encoding=COMPAT. We went through which APIs are affected and which are not, such as System.out, and lastly how to prepare code for the change. Next, read about the Java 18 Simple Web ServerClick to read tutorial: Java 18 Simple Web Server (jwebserver) Tutorial.Continue Reading Java 18 to 21 Tutorials
Overview of New Features in Java 18Click to read tutorial: Overview of New Features in Java 18 Java 18 Simple Web Server (jwebserver) TutorialClick to read tutorial: Java 18 Simple Web Server (jwebserver) Tutorial Java 18 Code Snippets in Javadoc with @snippetClick to read tutorial: Java 18 Code Snippets in Javadoc with @snippet Java 18 Deprecates Finalization: Use Cleaner InsteadClick to read tutorial: Java 18 Deprecates Finalization: Use Cleaner Instead Overview of New Features in Java 19Click to read tutorial: Overview of New Features in Java 19 Overview of New Features in Java 20Click to read tutorial: Overview of New Features in Java 20 Overview of New Features in Java 21Click to read tutorial: Overview of New Features in Java 21 Java 21 Virtual Threads Tutorial with ExamplesClick to read tutorial: Java 21 Virtual Threads Tutorial with Examples Java 21 Record Patterns with ExamplesClick to read tutorial: Java 21 Record Patterns with Examples
Overview of New Features in Java 18Click to read tutorial: Overview of New Features in Java 18 Java 18 Simple Web Server (jwebserver) TutorialClick to read tutorial: Java 18 Simple Web Server (jwebserver) Tutorial Java 18 Code Snippets in Javadoc with @snippetClick to read tutorial: Java 18 Code Snippets in Javadoc with @snippet Java 18 Deprecates Finalization: Use Cleaner InsteadClick to read tutorial: Java 18 Deprecates Finalization: Use Cleaner Instead Overview of New Features in Java 19Click to read tutorial: Overview of New Features in Java 19 Overview of New Features in Java 20Click to read tutorial: Overview of New Features in Java 20 Overview of New Features in Java 21Click to read tutorial: Overview of New Features in Java 21 Java 21 Virtual Threads Tutorial with ExamplesClick to read tutorial: Java 21 Virtual Threads Tutorial with Examples Java 21 Record Patterns with ExamplesClick to read tutorial: Java 21 Record Patterns with Examples