从byte[]中解析出int——如何正确地将byte转换为int

我们都知道, 在常用的JVM平台如Oracle JDK、OpenJDK中, int类型占用4个字节, 那么用如下代码是否就能正确地从byte[]解析出int了呢?

byte[] bytesOfInt = new byte[]{0x00, 0x00, 0x00, -0x01};
int result = ((int) bytesOfInt[3]      ) +
             ((int) bytesOfInt[2] <<  8) +
             ((int) bytesOfInt[1] << 16) +
             ((int) bytesOfInt[0] << 24);
System.out.println(result);

输出的结果为-1, 答案显然是不对的. 正确的做法是将result的计算方法改为:

int result = ((bytesOfInt[3] & 0xFF)      ) +
             ((bytesOfInt[2] & 0xFF) <<  8) +
             ((bytesOfInt[1] & 0xFF) << 16) +
             ((bytesOfInt[0]       ) << 24);

输出的结果为255. 那么为什么要对bytesOfInt中的低三个字节跟0xFF做按位与运算呢? 这就要牵扯到Java的补位机制和补码原理.

我们都知道字节在JVM中以补码的形式计算和存储, 首先快速回顾一下补码的概念.

  • 正数的补码与其原码、反码相同, 如byte b = 1的原码 = 00000001, 反码 = 00000001, 补码 = 00000001
  • 负数的补码是其反码加1的结果, 而负数的反码是其原码除符号位外, 按位取反的结果, 如byte b = -127的原码 = 11111111, 反码 = 10000000, 补码 = 10000001

而在Java中, byte转int的结果是在其补码的的高24位补符号位(正数补0, 负数补1), 即-127(byte)的补码为10000001, -127(int)的补码为11111111 11111111 11111111 10000001. 虽然它们表示的十进制数值是一样的(均为-127), 但是在byte到int的转换过程中, 其原始字节已经发生了变化. 因此, 这种转换对于位运算来说肯定是不对的. 解决方法就是先与0xFF做按位与运算, 例子如下:

-1(byte)的补码  =(11111111 11111111 11111111)10000001
0xFF(int)的补码 = 00000000 00000000 00000000 11111111
按位与的结果     = 00000000 00000000 00000000 10000001

看到这里, 是不是马上就能理解为什么bytesOfInt[0]不需要再跟0xFF做按位与运算了呢? 不得不说写JDK源代码的人就是扎实、严谨, 毕竟资源能省一点就是一点.

添加新评论