杰瑞科技汇

Java中16进制字符串如何转byte数组?

Java 16进制字符串转Byte数组终极指南:从入门到精通(附源码)

** 还在为Java中16进制与字节的转换烦恼吗?本文提供5种主流方法,从基础API到Apache Commons Lang,从性能对比到最佳实践,一篇搞定所有转换难题,助你成为团队处理16进制数据的大神。

Java中16进制字符串如何转byte数组?-图1
(图片来源网络,侵删)

(Meta Description)

本文详细讲解Java中将16进制字符串转换为Byte数组的多种方法,涵盖Byte.parseByte()BigIntegerStringBuilder以及第三方库Apache Commons LangHutool的实现,并提供完整代码示例和性能对比分析,是解决Java 16进制转byte问题的终极参考指南。


引言:为什么我们需要在Java中转换16进制和字节?

在Java开发中,我们经常会遇到需要处理16进制字符串的场景。

  • 通信协议开发: 很多硬件设备或网络协议(如Modbus、CAN总线)的数据传输常采用16进制格式。
  • 加密与解密: 像MD5、SHA等哈希算法的输出,以及AES等加密算法的密钥,通常以16进制字符串的形式表示和存储。
  • 数据存储与展示: 二进制文件(如图片、执行文件)的内容在日志或数据库中,常被表示为16进制字符串以便于阅读和调试。

Java的核心数据类型中并没有直接的“16进制字节”类型,我们需要将形如 "1A", "FF03" 这样的字符串,转换为对应的 byte 值,并最终存储在 byte[] 数组中,这个过程看似简单,但其中却暗藏不少“坑”。

我们就将彻底攻克这个难题,从最基础的方法到最高效的第三方库,全方位掌握Java 16进制转byte的技巧。

Java中16进制字符串如何转byte数组?-图2
(图片来源网络,侵删)

核心概念:理清16进制、字节与字符的关系

在开始编码前,我们先明确几个关键概念:

  1. 字节: Java中的 byte 是一个8位有符号整数,范围是 -128127,在内存中,它以补码形式存储。
  2. 16进制: 一种基数为16的数制,使用 0-9A-F(或 a-f)共16个符号来表示数值,每1个16进制字符(如 'A')代表4个二进制位(即半字节,Nibble)。
  3. 关系: 1个字节等于8个二进制位,恰好等于2个16进制字符。

"1A" 转换为 byte 的过程,本质上是将两个16进制字符 '1''A' 组合起来,还原成它们所代表的8位二进制数据,并最终存入一个 byte 变量中。


使用 Byte.parseByte() - 最基础的标准库方法

这是最直接、最符合Java语言规范的方法,其核心思想是,先将两个字符组成的16进制字符串,解析成一个 int 类型的十进制数,然后再将其转换为 byte 类型。

实现思路:

Java中16进制字符串如何转byte数组?-图3
(图片来源网络,侵删)
  1. 遍历16进制字符串,每次截取2个字符。
  2. 使用 Byte.parseByte(String s, int radix) 方法,radix 参数设为 16,表示按16进制解析。
  3. 注意: parseByte 方法解析出的值是 int 类型,其范围是 0-255,而 byte 的范围是 -128-127,当值大于127时,Java会自动进行类型收缩,将其转换为对应的负数。parseByte("FF", 16) 会得到 -1,因为 255 的二进制补码在8位中表示就是 -1,这通常是符合预期的。

代码示例:

public class HexToByteBasic {
    /**
     * 使用标准库 Byte.parseByte() 将16进制字符串转换为byte数组
     * @param hexString 16进制字符串,长度必须是偶数
     * @return byte数组
     * @throws IllegalArgumentException 如果字符串长度为奇数或包含非16进制字符
     */
    public static byte[] hexToByteArray(String hexString) {
        // 校验输入
        if (hexString == null || hexString.isEmpty()) {
            return new byte[0];
        }
        if (hexString.length() % 2 != 0) {
            throw new IllegalArgumentException("16进制字符串的长度必须是偶数");
        }
        int len = hexString.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            // 每次截取两个字符
            String byteHex = hexString.substring(i, i + 2);
            // 将16进制字符串解析为byte
            data[i / 2] = (byte) Integer.parseInt(byteHex, 16);
            // 或者使用 Byte.parseByte(byteHex, 16);
        }
        return data;
    }
    public static void main(String[] args) {
        String hexStr = "1A2B3C4D";
        byte[] result = hexToByteArray(hexStr);
        // 打印结果
        for (byte b : result) {
            System.out.printf("%02X ", b); // %02X 表示以大写16进制格式,两位不足补0
        }
        // 输出: 1A 2B 3C 4D
        String hexStrWithNegative = "FF00";
        byte[] resultWithNegative = hexToByteArray(hexStrWithNegative);
        for (byte b : resultWithNegative) {
            System.out.printf("%02X ", b);
        }
        // 输出: FF 00
        System.out.println("\nFF对应的byte值: " + resultWithNegative[0]); // 输出: -1
    }
}

优点:

  • 无需引入任何外部库,依赖标准JDK。
  • 代码直观,易于理解。

缺点:

  • 需要手动处理字符串截取和循环,代码量稍多。
  • 对于超长字符串,循环中的 substring 操作可能产生少量性能开销。

使用 BigInteger - 处理超大字符串的利器

如果你的16进制字符串非常长(例如超过8个字符,即超过4个字节),使用 Integer.parseInt() 会导致 NumberFormatException,因为 int 类型无法表示这么大的数,这时,BigInteger 就派上用场了。

实现思路:

  1. 将整个16进制字符串传递给 BigInteger 构造函数,指定 radix 为 16。
  2. BigInteger 对象会精确地表示这个大数。
  3. 使用 BigInteger.toByteArray() 方法,它会自动将大数转换为 byte 数组,这个方法会处理符号位,并且可能会在数组前添加一个 0x00 的字节来确保正数的最高位是0。

代码示例:

import java.math.BigInteger;
public class HexToByteBigInteger {
    public static byte[] hexToByteArray(String hexString) {
        if (hexString == null || hexString.isEmpty()) {
            return new byte[0];
        }
        BigInteger bigInteger = new BigInteger(hexString, 16);
        return bigInteger.toByteArray();
    }
    public static void main(String[] args) {
        String longHexStr = "00112233445566778899AABBCCDDEEFF";
        byte[] result = hexToByteArray(longHexStr);
        // 打印结果
        for (byte b : result) {
            System.out.printf("%02X ", b);
        }
        // 输出: 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
        // 注意:如果原字符串的最高位是1(即大于0x7F),BigInteger.toByteArray()
        // 会在结果前补一个0字节,以避免被误解为负数。
        String highBitHexStr = "FF112233";
        byte[] highBitResult = hexToByteArray(highBitHexStr);
        System.out.println("\n处理高位为1的字符串:");
        for (byte b : highBitResult) {
            System.out.printf("%02X ", b);
        }
        // 输出: 00 FF 11 22 33
    }
}

优点:

  • 极其强大,可以处理任意长度的16进制字符串。
  • 代码简洁,一行核心逻辑搞定。

缺点:

  • 引入了 java.math.BigInteger,对于短字符串来说有些“杀鸡用牛刀”。
  • 性能上,对于短字符串,通常比 parseByte 方法慢,因为它需要处理大数运算。
  • 需要注意 toByteArray() 可能会在数组前添加 0x00 字节,需要根据业务逻辑判断是否需要截取。

手动位移与或运算 - 性能极致追求者

如果你对性能有极致的要求,不希望有任何方法调用和对象创建的开销,那么手动进行位运算是最佳选择。

实现思路:

  1. 遍历16进制字符串的字符。
  2. 将每个16进制字符转换为它对应的4位二进制值(0-15)。
  3. 将第一个字符的4位值左移4位,然后与第二个字符的4位值进行按位或运算,组合成一个完整的8位字节。
  4. 将结果存入 byte 数组。

代码示例:

public class HexToByteManual {
    // 将16进制字符转换为对应的数值
    private static int charToByte(char c) {
        if (c >= '0' && c <= '9') {
            return c - '0';
        } else if (c >= 'A' && c <= 'F') {
            return c - 'A' + 10;
        } else if (c >= 'a' && c <= 'f') {
            return c - 'a' + 10;
        }
        throw new IllegalArgumentException("非法的16进制字符: " + c);
    }
    public static byte[] hexToByteArray(String hexString) {
        if (hexString == null || hexString.isEmpty()) {
            return new byte[0];
        }
        int len = hexString.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            // 将两个字符组合成一个字节
            byte high = (byte) (charToByte(hexString.charAt(i)) << 4);
            byte low = (byte) charToByte(hexString.charAt(i + 1));
            data[i / 2] = (byte) (high | low);
        }
        return data;
    }
    public static void main(String[] args) {
        String hexStr = "1A2B3C4D";
        byte[] result = hexToByteArray(hexStr);
        for (byte b : result) {
            System.out.printf("%02X ", b);
        }
        // 输出: 1A 2B 3C 4D
    }
}

优点:

  • 性能最高,没有额外的方法调用和对象创建,只有基础的位运算和数组访问。
  • 不依赖任何外部库。

缺点:

  • 代码可读性较差,对于不熟悉位运算的开发者来说难以维护。
  • 需要自己实现字符到数值的转换逻辑,容易出错(如大小写处理)。

方法四 & 五:使用第三方库 - 简洁高效的优雅之选

在实际项目中,我们更倾向于使用成熟、稳定、经过充分测试的第三方库来处理这类通用任务,它们通常经过高度优化,API设计优雅。

Apache Commons Lang

org.apache.commons.codec.binary.Hex 是处理16进制转换的“神器”。

Maven依赖:

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

代码示例:

import org.apache.commons.codec.binary.Hex;
public class HexToByteCommonsLang {
    public static void main(String[] args) {
        String hexStr = "1A2B3C4D";
        // HexUtils 提供了非常简洁的静态方法
        byte[] decodedBytes = Hex.decodeHex(hexStr.toCharArray());
        for (byte b : decodedBytes) {
            System.out.printf("%02X ", b);
        }
        // 输出: 1A 2B 3C 4D
    }
}

Hutool

Hutool是一个功能丰富、文档友好的Java工具类库,其 HexUtil 同样非常出色。

Maven依赖:

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.16</version> <!-- 请使用最新版本 -->
</dependency>

代码示例:

import cn.hutool.core.util.HexUtil;
public class HexToByteHutool {
    public static void main(String[] args) {
        String hexStr = "1A2B3C4D";
        // Hutool的API同样非常直观
        byte[] decodedBytes = HexUtil.decodeHex(hexStr);
        for (byte b : decodedBytes) {
            System.out.printf("%02X ", b);
        }
        // 输出: 1A 2B 3C 4D
    }
}

优点:

  • 代码极其简洁,一行代码搞定转换,可读性最高。
  • 底层实现经过高度优化,性能通常优于手写循环。
  • 功能全面,通常还提供反向转换(byte数组转16进制字符串)的功能。
  • 库经过大量项目验证,稳定可靠。

缺点:

  • 需要引入外部依赖,会增加项目的体积和复杂度(虽然通常很小)。

性能对比与最佳实践选择

方法 代码可读性 性能 适用场景 依赖
Byte.parseByte() 中等 较好 简单任务,不希望引入依赖
BigInteger 良好 较差 处理超长16进制字符串 JDK
手动位运算 最好 对性能有极致要求的底层代码
Apache Commons Lang 最好 企业级项目,追求代码优雅和稳定 第三方
Hutool 最好 已使用Hutool的项目或想快速集成功能 第三方

最佳实践建议:

  1. 日常开发首选: 如果你已经在使用 Apache Commons LangHutool,或者项目允许引入第三方库,强烈推荐使用它们,这是最优雅、最高效、最不容易出错的选择。
  2. 轻量级项目: 如果项目非常小,不希望引入任何依赖,Byte.parseByte() 循环法是最佳选择,它的性能足够好,代码也容易理解。
  3. 特殊场景: 如果你的业务需要处理超过8字节的超长16进制字符串,那么别无选择,必须使用 BigInteger
  4. 性能瓶颈: 如果你在处理高频、海量数据转换,并且经过性能分析确认转换是瓶颈,可以考虑手动位运算,但务必做好充分的测试和代码注释。

从“会用”到“精通”

从基础的 Byte.parseByte() 到强大的 BigInteger,再到优雅的第三方库,我们系统地掌握了在Java中将16进制字符串转换为 byte 数组的多种方法。

  • 入门级开发者应熟练掌握 Byte.parseByte() 循环法,理解其背后的原理。
  • 有经验的开发者应能根据项目需求,在性能、可读性和依赖之间做出明智的权衡,并优先考虑成熟的第三方库。
  • 专家级开发者则应能深入理解位运算的原理,并在性能关键路径上运用它,同时洞悉不同库的实现差异。

希望这篇详尽的指南能帮助你彻底解决“java 16进制转byte”的难题,并在实际工作中游刃有余,选择正确的工具,往往比写出复杂的代码更重要,去成为你团队中处理16进制数据的专家吧!

分享:
扫描分享到社交APP
上一篇
下一篇