1. 概述
本文将带你了解 Java 中将字节数组转换为十六进制字符串(hex string)的各种方式,以及反向操作的实现方式。
我们会从原理入手,逐步实现转换逻辑,并介绍几种常见的库级实现方案,帮助你根据场景选择最合适的方案。
2. 字节与十六进制之间的转换原理
2.1. 字节转十六进制
Java 中的 byte
是一个有符号的 8 位整数。因此,要将其转为十六进制字符串,我们需要 将高 4 位和低 4 位分别转为十六进制字符,然后拼接起来。这样每个字节会生成两个十六进制字符。
举个例子,45 的二进制是 0010 1101
,其十六进制表示就是 2d
:
0010 = 2 (十进制) = 2 (十六进制)
1101 = 13 (十进制) = d (十六进制)
所以:45 = 0010 1101 = 0x2d
下面是 Java 实现:
public String byteToHex(byte num) {
char[] hexDigits = new char[2];
hexDigits[0] = Character.forDigit((num >> 4) & 0xF, 16);
hexDigits[1] = Character.forDigit((num & 0xF), 16);
return new String(hexDigits);
}
我们来逐行分析:
创建一个长度为 2 的 char 数组,用于存放两个十六进制字符:
char[] hexDigits = new char[2];
提取高 4 位,并使用掩码
0xF
避免负数的符号扩展问题:hexDigits[0] = Character.forDigit((num >> 4) & 0xF, 16);
提取低 4 位:
hexDigits[1] = Character.forDigit((num & 0xF), 16);
最后将 char 数组转为字符串返回。
对于负数 -4
(二进制为 1111 1100
)的处理如下:
hexDigits[0]:
1111 1100 >> 4 = 1111 1111 1111 1111 1111 1111 1111 1111
1111 1111 1111 1111 1111 1111 1111 1111 & 0xF = 0000 0000 0000 0000 0000 0000 0000 1111 = 0xf
hexDigits[1]:
1111 1100 & 0xF = 0000 1100 = 0xc
所以:-4 = 1111 1100 = fc
⚠️ 注意:Character.forDigit()
方法返回的字符是小写形式。
2.2. 十六进制转字节
每个字节是 8 位,因此我们需要两个十六进制字符来表示一个字节。
举个例子:
Hexadecimal: 2d
2 = 0010 (二进制)
d = 1101 (二进制)
所以:2d = 0010 1101 = 45
Java 实现如下:
public byte hexToByte(String hexString) {
int firstDigit = toDigit(hexString.charAt(0));
int secondDigit = toDigit(hexString.charAt(1));
return (byte) ((firstDigit << 4) + secondDigit);
}
private int toDigit(char hexChar) {
int digit = Character.digit(hexChar, 16);
if(digit == -1) {
throw new IllegalArgumentException(
"Invalid Hexadecimal Character: "+ hexChar);
}
return digit;
}
分析:
提取两个字符并转为数字:
int firstDigit = toDigit(hexString.charAt(0)); int secondDigit = toDigit(hexString.charAt(1));
左移高 4 位,然后加上低 4 位:
return (byte) ((firstDigit << 4) + secondDigit);
toDigit()
方法使用Character.digit()
来转换字符。如果字符不是合法的十六进制字符,返回 -1,我们在这里抛出异常。
3. 字节数组与十六进制字符串的转换
3.1. 字节数组转十六进制字符串
遍历字节数组,对每个字节调用 byteToHex
方法:
public String encodeHexString(byte[] byteArray) {
StringBuffer hexStringBuffer = new StringBuffer();
for (int i = 0; i < byteArray.length; i++) {
hexStringBuffer.append(byteToHex(byteArray[i]));
}
return hexStringBuffer.toString();
}
✅ 输出始终是小写。
3.2. 十六进制字符串转字节数组
首先判断字符串长度是否为偶数(否则无法成对解析),然后每两个字符组成一个字节:
public byte[] decodeHexString(String hexString) {
if (hexString.length() % 2 == 1) {
throw new IllegalArgumentException(
"Invalid hexadecimal String supplied.");
}
byte[] bytes = new byte[hexString.length() / 2];
for (int i = 0; i < hexString.length(); i += 2) {
bytes[i / 2] = hexToByte(hexString.substring(i, i + 2));
}
return bytes;
}
4. 使用 BigInteger 实现转换
4.1. 字节数组转十六进制字符串
public String encodeUsingBigIntegerStringFormat(byte[] bytes) {
BigInteger bigInteger = new BigInteger(1, bytes);
return String.format(
"%0" + (bytes.length << 1) + "x", bigInteger);
}
✅ 输出是小写,且补零对齐。
也可以使用 toString(16)
,但不会补零:
public String encodeUsingBigIntegerToString(byte[] bytes) {
BigInteger bigInteger = new BigInteger(1, bytes);
return bigInteger.toString(16);
}
4.2. 十六进制字符串转字节数组
public byte[] decodeUsingBigInteger(String hexString) {
byte[] byteArray = new BigInteger(hexString, 16)
.toByteArray();
if (byteArray[0] == 0) {
byte[] output = new byte[byteArray.length - 1];
System.arraycopy(
byteArray, 1, output,
0, output.length);
return output;
}
return byteArray;
}
⚠️ 注意:toByteArray()
会多出一个符号位,需要手动处理。
5. 使用 JAXB 的 DatatypeConverter
public String encodeUsingDataTypeConverter(byte[] bytes) {
return DatatypeConverter.printHexBinary(bytes);
}
public byte[] decodeUsingDataTypeConverter(String hexString) {
return DatatypeConverter.parseHexBinary(hexString);
}
✅ 输出是大写。
⚠️ Java 9+ 需要显式引入 java.xml.bind
模块。
6. 使用 Apache Commons Codec
public String encodeUsingApacheCommons(byte[] bytes)
throws DecoderException {
return Hex.encodeHexString(bytes);
}
public byte[] decodeUsingApacheCommons(String hexString)
throws DecoderException {
return Hex.decodeHex(hexString);
}
✅ 输出是小写。
7. 使用 Google Guava
public String encodeUsingGuava(byte[] bytes) {
return BaseEncoding.base16().encode(bytes);
}
public byte[] decodeUsingGuava(String hexString) {
return BaseEncoding.base16()
.decode(hexString.toUpperCase());
}
✅ 默认是大写编码,如需小写可使用 .lowerCase()
。
8. Java 17 新特性:HexFormat
8.1. 字节数组转十六进制字符串
public String encodeUsingHexFormat(byte[] bytes) {
HexFormat hexFormat = HexFormat.of();
return hexFormat.formatHex(bytes);
}
8.2. 十六进制字符串转字节数组
public byte[] decodeUsingHexFormat(String hexString) {
HexFormat hexFormat = HexFormat.of();
return hexFormat.parseHex(hexString);
}
✅ Java 17 引入的标准 API,简单直观。
9. 总结
方法 | 优点 | 缺点 |
---|---|---|
手动实现 | 无依赖,可控性强 | 代码量稍大 |
BigInteger | 简洁 | 需处理符号位 |
DatatypeConverter | 简单易用 | Java 9+ 需模块支持 |
Commons Codec | 稳定成熟 | 需引入依赖 |
Guava | 功能丰富 | 需引入依赖 |
HexFormat (Java 17) | 标准化,简洁 | JDK 版本要求高 |
✅ 如果你没有使用外部库,推荐手动实现;如果项目已引入相关库,直接使用即可。避免为了两个工具方法而引入整个库。