杰瑞科技汇

Java十六进制byte数组如何转换?

核心概念

  1. byte 的范围: Java 中的 byte 是一个 8 位有符号整数,其范围是 -128127
  2. 十六进制表示: 一个 byte (8位) 可以用两个十六进制字符(4位一组)来表示。0x0F (15) 和 0xF0 (240)。
  3. 负数的表示: Java 中负数的二进制是使用“补码”表示的。-1byte 值是 0b11111111,其二进制补码就是它自己,这个 byte 转换为十六进制就是 0xFF

使用 Java 8+ 内置的 Hex 类 (推荐)

这是最现代、最简洁、最安全的方法,无需引入第三方库,Java 8 引入了 java.util.Base64,但没有直接提供 Hex 类,在 java.util 包下有一个隐藏的 Hex 类,我们可以通过反射来使用它,或者,更常见的做法是使用 Apache Commons Codec 或 Guava 库。

Java十六进制byte数组如何转换?-图1
(图片来源网络,侵删)

方案 1.1: 使用反射调用 java.util.Hex (无依赖)

这是一个巧妙的方法,可以避免引入外部库。

import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
public class HexUtilWithReflection {
    private static final Method ENCODE_METHOD;
    private static final Method DECODE_METHOD;
    static {
        try {
            // 获取 java.util.Hex 类
            Class<?> hexClass = Class.forName("java.util.Hex");
            // 获取 encodeHex 方法
            ENCODE_METHOD = hexClass.getMethod("encodeHex", byte[].class);
            // 获取 decodeHex 方法
            DECODE_METHOD = hexClass.getMethod("decodeHex", char[].class);
        } catch (Exception e) {
            throw new RuntimeException("Failed to initialize HexUtil", e);
        }
    }
    /**
     * 将 byte[] 转换为十六进制字符串
     * @param bytes 字节数组
     * @return 十六进制字符串 ( "48656C6C6F")
     */
    public static String bytesToHex(byte[] bytes) {
        if (bytes == null) {
            return null;
        }
        try {
            char[] chars = (char[]) ENCODE_METHOD.invoke(null, bytes);
            return new String(chars);
        } catch (Exception e) {
            throw new RuntimeException("Failed to encode bytes to hex", e);
        }
    }
    /**
     * 将十六进制字符串转换为 byte[]
     * @param hexString 十六进制字符串
     * @return 字节数组
     */
    public static byte[] hexToBytes(String hexString) {
        if (hexString == null || hexString.length() % 2 != 0) {
            throw new IllegalArgumentException("Hex string must be non-null and have an even length");
        }
        try {
            char[] chars = hexString.toCharArray();
            return (byte[]) DECODE_METHOD.invoke(null, chars);
        } catch (Exception e) {
            throw new RuntimeException("Failed to decode hex to bytes", e);
        }
    }
    public static void main(String[] args) {
        String originalString = "Hello, World! 你好";
        byte[] bytes = originalString.getBytes(StandardCharsets.UTF_8);
        // byte[] -> Hex String
        String hexString = bytesToHex(bytes);
        System.out.println("Hex String: " + hexString);
        // Hex String -> byte[]
        byte[] decodedBytes = hexToBytes(hexString);
        String decodedString = new String(decodedBytes, StandardCharsets.UTF_8);
        System.out.println("Decoded String: " + decodedString);
    }
}

方案 1.2: 使用 Apache Commons Codec (业界标准)

这是最流行、最可靠的方法,几乎所有项目都会用到这个库。

添加 Maven 依赖:

<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.15</version> <!-- 使用最新版本 -->
</dependency>

使用 Hex 类:

import org.apache.commons.codec.binary.Hex;
public class HexUtilWithCommonsCodec {
    public static void main(String[] args) {
        String originalString = "Hello, World!";
        byte[] bytes = originalString.getBytes(StandardCharsets.UTF_8);
        // byte[] -> Hex String
        // Hex.encodeHexString() 方法直接返回 String
        String hexString = Hex.encodeHexString(bytes);
        System.out.println("Hex String: " + hexString); // 输出: 48656c6c6f2c20576f726c6421
        // Hex String -> byte[]
        // Hex.decodeHex() 方法接收一个 char[] 或 String
        try {
            byte[] decodedBytes = Hex.decodeHex(hexString.toCharArray());
            String decodedString = new String(decodedBytes, StandardCharsets.UTF_8);
            System.out.println("Decoded String: " + decodedString); // 输出: Hello, World!
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

手动实现 (面试和底层理解)

手动实现有助于理解底层原理,但在生产环境中不推荐,因为它容易出错且性能可能不如优化过的库。

方案 2.1: byte[]String

public class ManualByteToHex {
    private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
    public static String bytesToHex(byte[] bytes) {
        if (bytes == null) {
            return null;
        }
        // 创建一个 char 数组,每个 byte 对应两个字符
        char[] hexChars = new char[bytes.length * 2];
        for (int i = 0; i < bytes.length; i++) {
            // 将 byte 转换为无符号整数 (0-255)
            int v = bytes[i] & 0xFF;
            // 取高4位和低4位,分别转换为字符
            hexChars[i * 2] = HEX_ARRAY[v >>> 4]; // >>> 是无符号右移
            hexChars[i * 2 + 1] = HEX_ARRAY[v & 0x0F];
        }
        return new String(hexChars);
    }
    public static void main(String[] args) {
        byte[] bytes = {0x48, 0x65, 0x6C, 0x6C, 0x6F, (byte) 0xFF}; // (byte) 0xFF 是 -1
        String hexString = bytesToHex(bytes);
        System.out.println("Hex String: " + hexString); // 输出: 48656C6C6C6FFF
    }
}

方案 2.2: Stringbyte[]

public class ManualHexToByte {
    public static byte[] hexToBytes(String hexString) {
        if (hexString == null || hexString.length() % 2 != 0) {
            throw new IllegalArgumentException("Invalid hex string.");
        }
        byte[] bytes = new byte[hexString.length() / 2];
        for (int i = 0; i < bytes.length; i++) {
            // 每两个字符解析为一个 byte
            int highNibble = Character.digit(hexString.charAt(i * 2), 16); // 取高4位
            int lowNibble = Character.digit(hexString.charAt(i * 2 + 1), 16); // 取低4位
            bytes[i] = (byte) ((highNibble << 4) | lowNibble);
        }
        return bytes;
    }
    public static void main(String[] args) {
        String hexString = "48656C6C6C6FFF";
        byte[] bytes = hexToBytes(hexString);
        // 打印 byte 数组内容
        for (byte b : bytes) {
            System.out.printf("%02X ", b); // %02X 保证输出两位大写十六进制
        }
        System.out.println();
    }
}

使用 BigInteger (简单但性能略低)

这个方法非常简洁,适合一次性转换,但循环性能不如手动方法。

方案 3.1: byte[]String

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
public class BigIntegerByteToHex {
    public static String bytesToHex(byte[] bytes) {
        if (bytes == null) {
            return null;
        }
        // 将 byte[] 视为一个正数,然后转换为十六进制字符串
        BigInteger bigInt = new BigInteger(1, bytes); // 参数 1 表示正数
        return bigInt.toString(16);
    }
    public static void main(String[] args) {
        byte[] bytes = {0x00, 0x01, 0x02, (byte) 0xFF};
        String hexString = bytesToHex(bytes);
        System.out.println("Hex String: " + hexString); // 输出: 102ff
        // 注意:前导零会被省略
    }
}

方案 3.2: Stringbyte[]

import java.math.BigInteger;
public class BigIntegerHexToByte {
    public static byte[] hexToBytes(String hexString) {
        if (hexString == null) {
            return null;
        }
        BigInteger bigInt = new BigInteger(hexString, 16);
        // toByteArray() 会返回一个 byte[],但可能包含一个前导的 0x00,因为 BigInteger 是任意精度的
        byte[] bytes = bigInt.toByteArray();
        // 处理前导零的问题
        // "FF" -> byte[] { -1 },但 "00FF" -> byte[] { 0, -1 }
        // 如果原始字符串长度是奇数,说明丢失了一个前导零
        if (hexString.length() % 2 != 0) {
            byte[] temp = new byte[bytes.length + 1];
            System.arraycopy(bytes, 0, temp, 1, bytes.length);
            temp[0] = 0;
            return temp;
        }
        return bytes;
    }
    public static void main(String[] args) {
        String hexString = "102ff";
        byte[] bytes = hexToBytes(hexString);
        for (byte b : bytes) {
            System.out.printf("%02X ", b);
        }
        System.out.println(); // 输出: 01 02 FF
    }
}

总结与推荐

方法 优点 缺点 推荐场景
Apache Commons Codec 代码简洁、性能高、稳定可靠、业界标准 需要引入第三方依赖 生产环境首选
手动实现 无依赖、有助于理解底层 代码繁琐、易出错、性能需自己优化 学习、面试、无法引入依赖的极端环境
BigInteger 代码非常简洁 性能较低、有前导零等边界问题处理麻烦 一次性转换、代码行数要求极低的场景
反射调用 java.util.Hex 无依赖、是 Java 官方实现 使用反射,不够优雅,未来可能失效 不想引入第三方库,且愿意使用“黑科技”的场景

最终建议:

  • 如果你正在构建一个新的项目或可以使用 Maven/Gradle强烈推荐使用 Apache Commons Codec,这是最专业、最不容易出错的选择。
  • 如果你在面试中被问到这个问题,展示手动实现的能力非常重要,这能体现你对数据类型的深刻理解。
  • 如果你在一个无法添加依赖的旧项目中,手动实现是唯一的选择。
分享:
扫描分享到社交APP
上一篇
下一篇