I can replicate your problem: printing works correctly when running your code if compiled with JDK 17, and fails when running your code if compiled with JDK 18.
One of the changes implemented in Java 18 was JEP 400: UTF-8 by Default. The summary for that JEP stated:
Specify UTF-8 as the default charset of the standard Java APIs. With
this change, APIs that depend upon the default charset will behave
consistently across all implementations, operating systems, locales,
and configurations.
That sounds good, except one of the goals of that change was (with my emphasis added):
Standardize on UTF-8 throughout the standard Java APIs, except for
console I/O.
So I think your problem arose because you had ensured that the console's encoding in Intellij IDEA was UTF-8, but the PrintStream
that you were using to write to that console (i.e. System.out
) was not.
The Javadoc for PrintStream
states (with my emphasis added):
All characters printed by a PrintStream are converted into bytes using
the given encoding or charset, or the default charset if not
specified.
Since your PrintStream
was System.out
, you had not specified any "encoding or charset", and were therefore using the "default charset", which was presumably not UTF-8. So to get your code to work on Java 18, you just need to ensure that your PrintStream
is encoding with UTF-8. Here's some sample code to show the problem and the solution:
package pkg;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
public class Humpty {
public static void main(String[] args) throws java.io.UnsupportedEncodingException {
char letter = 'ᚙ';
String charset1 = System.out.charset().displayName(); // charset() requires JDK 18
System.out.println("Writing the character " + letter + " to a PrintStream with charset " + charset1); // fails
PrintStream ps = new PrintStream(new FileOutputStream(FileDescriptor.out), true, StandardCharsets.UTF_8);
String charset2 = ps.charset().displayName(); // charset() requires JDK 18
ps.println("Writing the character " + letter + " to a PrintStream with charset " + charset2); // works
}
}
This is the output in the console when running that code:
C:\Java\jdk-18\bin\java.exe -javaagent:C:\Users\johndoe\AppData\Local\JetBrains\Toolbox\apps\IDEA-U\ch-0\221.5080.93\lib\idea_rt.jar=64750:C:\Users\johndoe\AppData\Local\JetBrains\Toolbox\apps\IDEA-U\ch-0\221.5080.93\bin -Dfile.encoding=UTF-8 -classpath C:\Users\johndoe\IdeaProjects\HelloIntellij\out\production\HelloIntellij pkg.Humpty
Writing the character ? to a PrintStream with charset windows-1252
Writing the character ᚙ to a PrintStream with charset UTF-8
Process finished with exit code 0
Notes:
PrintStream
has a new method in Java 18 named charset()
which "returns the charset used in this PrintStream instance". The code above calls charset(), and shows that for my machine my "default charset" is windows-1252, not UTF-8.
- I used Intellij IDEA 2022.1 Beta (Ultimate Edition) for testing.
- In the console I used font DejaVu Sans to ensure that the character "ᚙ" could be rendered.
UPDATE: To address the issue raised in the comments below by Mostafa Zeinali, the PrintStream
used by System.out
can be redirected to a UTF-8 PrintStream
by calling System.setOut()
. Here's sample code:
String charsetOut = System.out.charset().displayName();
if (!"UTF-8".equals(charsetOut)) {
System.out.println("The charset for System.out is " + charsetOut + ". Changing System.out to use charset UTF-8");
System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out), true, StandardCharsets.UTF_8));
System.out.println("The charset for System.out is now " + System.out.charset().displayName());
}
This is the output from that code on my Windows 10 machine:
The charset for System.out is windows-1252. Changing System.out to use charset UTF-8
The charset for System.out is now UTF-8
Note that System.out
is a final
variable, so you can't directly assign a new PrintStream
to it. This code fails to compile with the error "Cannot assign a value to final variable 'out'":
System.out = new PrintStream(new FileOutputStream(FileDescriptor.out), true, StandardCharsets.UTF_8); // Won't compile