How do I convert byte arrays to hexadecimal and back in Java 17?

When you inspect binary data, a hexadecimal string can be easier to read than a list of decimal byte values. You might use it when troubleshooting a file format or checking a message exchanged between applications.

This tutorial shows a complete conversion in both directions, including readable formatting and invalid input handling.

1. Choose a hexadecimal format

Java 17 introduced java.util.HexFormat as a standard API. No preview or incubator flags are needed.

Use HexFormat.of() for lowercase output without separators. For uppercase output with colons between bytes, use:

HexFormat readable =
        HexFormat.ofDelimiter(":").withUpperCase();

Each byte becomes two hexadecimal digits. These options are documented in the Java 17 HexFormat API.

2. Create the complete example

Save the following as HexFormatExample.java:

import java.util.Arrays;
import java.util.HexFormat;

public class HexFormatExample {
    public static void main(String[] args) {
        byte[] packet = {
                0, 15, 16, 127, (byte) 128, (byte) 255
        };

        HexFormat plain = HexFormat.of();
        HexFormat readable =
                HexFormat.ofDelimiter(":").withUpperCase();

        System.out.println(
                "Compact: " + plain.formatHex(packet));

        String formatted = readable.formatHex(packet);
        System.out.println("Readable: " + formatted);

        byte[] restored = readable.parseHex(formatted);
        System.out.println(
                "Same bytes: " + Arrays.equals(packet, restored));
        System.out.println(
                "Java bytes: " + Arrays.toString(restored));

        try {
            readable.parseHex("00:GG");
        } catch (IllegalArgumentException exception) {
            System.out.println(
                    "Invalid input: expected hexadecimal byte pairs");
        }
    }
}

The program first creates sample data. It then prints two representations, parses the colon-separated version, and checks whether the recovered bytes match the original.

3. Compile and run

Open a terminal in the directory containing the file:

javac --release 17 HexFormatExample.java
java HexFormatExample

Output:

Compact: 000f107f80ff
Readable: 00:0F:10:7F:80:FF
Same bytes: true
Java bytes: [0, 15, 16, 127, -128, -1]
Invalid input: expected hexadecimal byte pairs

4. Understand the result

The two formatted strings represent identical data. Java’s signed byte representation explains the negative decimal values in the recovered array: the bit patterns 80 and FF correspond to -128 and -1.

Arrays.equals() confirms that the conversion preserved the bytes.

Parsing must use the expected delimiters. Invalid digits, such as GG, cause IllegalArgumentException; hexadecimal digit letters themselves are case-insensitive. See HexFormat.parseHex().

Best practices

  • Define the expected representation when exchanging data with another application.
  • Use a matching formatter for encoding and decoding.
  • Keep the original bytes when your next operation requires binary data.
  • Handle invalid external input at the point where it enters your application.
  • Remember that hexadecimal encoding makes data readable; it does not encrypt it.

Summary

Use formatHex() to turn bytes into hexadecimal text and parseHex() to recover them. A short round-trip example helps verify your chosen representation before integrating it into a larger application.