字節數組 (`byte[]`) 轉十六進制字符串是加密、哈希、網絡協議、日誌記錄的高頻操作。看似簡單,實則蘊含**有符號 byte 陷阱**、**位運算優化**、**JDK 版本演進**、**效能數量級差異**。本文從底層原理到現代最佳實踐,全方位解析這一「基礎中的基礎」操作。
十六進制表示範圍 `0x00` ~ `0xFF` (0~255)。Java `byte` 最高位為符號位,直接轉 `int` 會**符號擴展**:
byte b = (byte) 0xAB; // 二進制: 1010 1011, 值為 -85 (補碼)
int i = b; // 符號擴展: 0xFFFFFFAB (十進制 -85)
Integer.toHexString(i) // "ffffffab" (8 位,含符號位擴展) ❌
解決:`b & 0xFF` 強制無符號擴展,保留低 8 位。
int unsigned = b & 0xFF; // 0x000000AB (十進制 171) ✅
Integer.toHexString(unsigned) // "ab" (可能只有 1 位,需補零)
// 經典雙字符查表法 (無分支、無格式化開銷)
private static final char[] HEX_CHARS = "0123456789abcdef".toCharArray();
public static String bytesToHexClassic(byte[] bytes) {
char[] result = new char[bytes.length * 2];
for (int i = 0; i < bytes.length; i++) {
int v = bytes[i] & 0xFF;
result[i * 2] = HEX_CHARS[v >>> 4]; // 高 4 位
result[i * 2 + 1] = HEX_CHARS[v & 0x0F]; // 低 4 位
}
return new String(result);
}
// 絕對不要在循環中使用!
StringBuilder sb = new StringBuilder();
for (byte b : bytes) sb.append(String.format("%02x", b)); // 慢 100 倍以上
// JDK 17+ 標準庫 java.util.HexFormat
import java.util.HexFormat;
public static String bytesToHexModern(byte[] bytes) {
return HexFormat.of().formatHex(bytes); // 默認小寫、無分隔符
// 可配置:
// HexFormat.ofDelimiter(":").withUpperCase().formatHex(bytes) // "AA:BB:CC"
}
// Java SE 內置 (Java 6-16),Java 11+ 需額外依賴 jakarta.xml.bind-api
import javax.xml.bind.DatatypeConverter;
public static String bytesToHexJaxb(byte[] bytes) {
return DatatypeConverter.printHexBinary(bytes); // 大寫、無分隔符
}
// Guava
com.google.common.io.BaseEncoding.base16().lowerCase().encode(bytes)
// Apache Commons Codec
org.apache.commons.codec.binary.Hex.encodeHexString(bytes)
// Netty (高性能網絡框架內部實現)
io.netty.buffer.ByteBufUtil.hexDump(bytes)
| 實現方式 | 相對吞吐量 (基準=1.0) | 分配率 (MB/sec) | 推薦場景 |
|---|---|---|---|
| `HexFormat.of().formatHex()` (JDK 17+) | **1.0 (最快)** | 極低 | **JDK 17+ 首選** |
| 手工查表 `HEX_CHARS` | 0.95 - 1.05 | 極低 | JDK 8/11 自實現首選 |
| Netty `ByteBufUtil.hexDump` | 0.9 | 低 | Netty 項目 |
| Guava `BaseEncoding` | 0.7 | 低 | Guava 項目 |
| Commons Codec `Hex` | 0.5 | 中 | 舊項目兼容 |
| `DatatypeConverter` | 0.4 | 中 | Java 6-10 標準庫 |
| `String.format("%02x")` 循環 | **0.01 (慢 100 倍)** | 極高 | **嚴禁生產使用** |
// JDK 17+
byte[] bytes = HexFormat.of().parseHex("aabbcc");
// 手工 (JDK 8+)
public static byte[] hexToBytes(String hex) {
int len = hex.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
+ Character.digit(hex.charAt(i+1), 16));
}
return data;
}
字節轉十六進制,**核心難點是有符號 byte 的無符號擴展 (`& 0xFF`)**。**現代最優解是 JDK 17 `HexFormat.of().formatHex(bytes)`**,標準、快、功能全。JDK 8/11 項目請複製**手工查表法** (HEX_CHARS 雙字符寫入),效能與 `HexFormat` 齊平,零依賴。堅決拒絕 `String.format` 循環,這是效能殺手。理解位運算原理,不再為「為什麼輸出 ffffffab」而困惑。