In Java, hex text is a printable view of a byte[]. Conversion is: hex string → bytes → new String(bytes, charset). The charset choice is the difference between correct Unicode and mojibake.
Online check: Hex to String. Overview: hexadecimal encoding.
Java 17+: HexFormat
import java.util.HexFormat;
import java.nio.charset.StandardCharsets;
byte[] bytes = HexFormat.of().parseHex("48656c6c6f");
String text = new String(bytes, StandardCharsets.UTF_8); // Hello
String hex = HexFormat.of().formatHex("你好".getBytes(StandardCharsets.UTF_8));
HexFormat is the clearest modern API. Prefer it when you can require Java 17+.
Older Java: manual parse
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
static byte[] hexToBytes(String hex) {
String clean = hex.replaceAll("\\s+", "");
if ((clean.length() & 1) != 0) {
throw new IllegalArgumentException("odd hex length");
}
byte[] out = new byte[clean.length() / 2];
for (int i = 0; i < clean.length(); i += 2) {
out[i / 2] = (byte) Integer.parseInt(clean.substring(i, i + 2), 16);
}
return out;
}
static String hexToString(String hex, Charset cs) {
return new String(hexToBytes(hex), cs);
}
System.out.println(hexToString("e4bda0e5a5bd", StandardCharsets.UTF_8));
Encoding rules that prevent bugs
- Always pass an explicit
Charset(orStandardCharsets.UTF_8). Relying on the platform default is a production foot-gun. - UTF-16 text in hex includes byte order; if a BOM is present, handle it deliberately.
- Binary payloads (keys, digests) should stay as
byte[]— do not force a text decode.
Validation
if (!clean.matches("(?i)[0-9a-f]*") || clean.length() % 2 != 0) {
throw new IllegalArgumentException("invalid hex");
}
Related language notes: JavaScript · C# · PHP.