When developing Java applications on z/OS, it’s common to deal with EBCDIC-encoded input and output files. These files are read, processed, and written by Java programs that run directly on the mainframe. In such environments, even small changes in Java’s platform behavior can have significant effects.
This blog post highlights a critical and easily overlooked difference between Java 17 and Java 21 on z/OS when it comes to character encoding—and how it can cause files to become unreadable or incompatible. (see JEP 400: UTF-8 by Default)
The Setup: Java Program Reading and Writing EBCDIC Files
We’re using a basic Java program to read an EBCDIC-encoded file and write it back line by line:
import java.io.*;
public class FileReaderTest {
public static void main(String[] args) {
String source = "ebcdic-input.txt";
String destination = "output.txt";
try {
BufferedReader reader = new BufferedReader(new FileReader(source));
BufferedWriter writer = new BufferedWriter(new FileWriter(destination));
String row;
while ((row = reader.readLine()) != null) {
System.out.println(row); // Console output
writer.write(row); // Write to output file
writer.newLine(); // Add newline
}
System.out.println("The file was successfully copied to '" + destination + "'");
reader.close();
writer.close();
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
}
}
}
This program looks harmless. It doesn’t specify any encoding—it simply uses Java’s default behavior for reading and writing text files.
The Problem: Different Results with Java 17 and Java 21
On Java 17, this program works as expected on z/OS: the output file contains the same EBCDIC content as the input.
On Java 21, the exact same program—reading the same input and writing to the same file—produces a completely different result. The output is no longer valid EBCDIC. Other programs or systems expecting EBCDIC content will not be able to read the file correctly.
This isn’t just about incorrect display or formatting: the output file is actually corrupted because it is encoded in a different format than intended.
Root Cause: Default Charset Behavior Changed in Java 21
The underlying issue is that Java 21 has changed the default encoding behavior compared to Java 17.
- In Java 17, the default charset on z/OS is based on the platform and locale. For example,
file.encoding=IBM1047will be used unless overridden. - In Java 21, the default charset is always UTF-8, regardless of the platform or environment.
This means:
new FileReader("input.txt");
new FileWriter("output.txt");
…will use EBCDIC on Java 17, but UTF-8 on Java 21.
If the input file is EBCDIC, the Java 21 program will misinterpret it while reading—or write UTF-8 when EBCDIC is expected. The result is incompatible or unreadable output.
Solution: Specify Encodings Explicitly
To ensure consistent behavior across Java versions, you must not rely on default encodings.
Option 1: Temporary migration workaround using runtime flags
Use the -Dfile.encoding option to force the desired encoding (e.g., IBM1047) at startup:
$JAVA_21_BIN/java -Dfile.encoding=IBM1047 FileReaderTest
Alternatively, you can use:
$JAVA_21_BIN/java -Dfile.encoding=COMPAT FileReaderTest
⚠️ Note: The
COMPATsetting restores locale-based behavior similar to Java 17 and is useful during migration. However, we recommend using it only as a temporary workaround.
Option 2: Preferred approach – set encoding explicitly in code
This makes the behavior explicit and independent of runtime settings:
BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(source), "IBM1047"));
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(destination), "IBM1047"));
This is the most robust way to ensure platform consistency.
Strategic Recommendation: Migrate to UTF-8
While the compatibility settings and platform encodings are useful for maintaining legacy behavior, we strongly recommend a medium-term migration to UTF-8 for all Java applications.
UTF-8 has become the standard encoding across platforms, tools, APIs, and data exchange formats. Explicitly adopting UTF-8:
- Simplifies interoperability with distributed systems
- Avoids platform-specific edge cases
- Future-proofs your code for upcoming Java releases and cross-platform environments
Conclusion
When working with EBCDIC files on z/OS, the move from Java 17 to Java 21 introduces a significant change: UTF-8 becomes the default encoding, potentially corrupting files if not handled properly.
Key Takeaways
- Java 17 respects z/OS encodings like IBM1047 by default.
- Java 21 defaults to UTF-8, even on z/OS.
- As a result, EBCDIC files may be corrupted or unreadable if encoding is not handled explicitly.
- Always define the charset explicitly in your code or set it via JVM options.
- Long-term recommendation: migrate toward UTF-8 usage in applications wherever possible.
Transparency Note
This blog post was drafted with the support of generative AI to help structure and formulate the content. However, the technical background was thoroughly researched by our team beforehand, and we consider the topic highly relevant and worth sharing. The final content has been carefully reviewed and approved by us before publication.
