
java 的 byte 类型是有符号的 8 位整数,当它参与算术运算被自动提升为 int 时会进行符号扩展,导致高位填充 1(而非 0),这是将字节数组解析为整数时出现意外结果的根本原因。
在 Java 中,byte 类型的取值范围是 -128 到 127(即有符号 8 位补码表示)。十六进制字面量 0x95 对应十进制 149,超出了 byte 的正向表示上限,因此强制转换 (byte)0x95 实际得到的是 -107(因为 0x95 & 0xFF == 149,而 149 - 256 == -107)。
关键问题出在类型提升阶段:当表达式 bytes[0] + (bytes[1] 隐式提升为 int。而 Java 的规则是:有符号类型提升采用符号扩展(sign extension)。
例如:
byte b = (byte) 0x95; // b == -107 int i = b; // i == 0xFFFFFF95 (-107 in 32-bit two's complement)
因此,原代码:
int number = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // 其中 bytes[0] 被提升为 0xFFFFFF95(即 -107),而非期望的 0x00000095 // 计算过程实际为:(-107) + (0x19 << 8) + (0x07 << 16) + 0 // = -107 + 6400 + 458752 + 0 = 465045 → 十六进制为 0x00071895 ✅(与你观察一致)
而你期望的 0x00071995(即 465301)比实际结果大 256 —— 这恰好是 0x95 与 -0x6B(即 -107)之间的模 256 差值在高位传播后的影响。
✅ 正确做法:消除符号扩展影响,强制按无符号方式解释字节。常用且推荐的方式是对每个 byte 先转为 int,再与 0xFF 按位与:
立即学习“Java免费学习笔记(深入)”;
int number = (bytes[0] & 0xFF)
| ((bytes[1] & 0xFF) << 8)
| ((bytes[2] & 0xFF) << 16)
| ((bytes[3] & 0xFF) << 24);
// 结果:0x00071995(465301)? 原理说明:bytes[0] & 0xFF 将 byte 提升为 int 后再屏蔽高 24 位,等效于零扩展(zero extension),确保 0x95 始终被当作 149 处理。
⚠️ 注意事项:
- 不要仅靠 (int)bytes[i],这仍会触发符号扩展;
- 若需处理大端序(Big-Endian)数据(如网络字节序),上述位移顺序对应小端序(LSB 在前);若字节数组是大端序(如 0x00, 0x07, 0x19, 0x95),则应调整位移:bytes[0]
- Java 9+ 提供了更安全的工具方法:Integer.BYTES 配合 ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt(),可避免手动位运算错误。
总结:Java 中字节到整数的转换不是“直接拼接”,而是受类型系统(尤其是有符号性与提升规则)严格约束的过程。理解 & 0xFF 的本质——将字节无符号化——是写出健壮二进制解析逻辑的关键一步。










