了解Java字节

因此,在昨天的工作中,我不得不编写一个应用程序来计算AFP文件中的页面。 所以我除掉了我的MO:DCA规范PDF并找到了结构化字段BPG (Begin Page)及其3字节标识符。 该应用程序需要在AIX机器上运行,因此我决定用Java编写它。

为了获得最大效率,我决定读取每个结构化字段的前6个字节,然后跳过字段中的剩余字节。 这会让我:

 0: Start of field byte 1-2: 2-byte length of field 3-5: 3-byte sequence identifying the type of field 

所以我检查了字段类型并增加了一个页面计数器(如果它是BPG ,如果不是,我就不会。 然后我跳过字段中的剩余字节而不是通读它们。 在这里,跳过(实际上是字段长度)是我发现Java使用带符号字节的地方。

我做了一些谷歌搜索,发现了很多有用的信息。 当然,最有用的是执行按位& 0xff来获取unsigned int值的指令。 这对我来说是必要的,以获得可用于计算要跳过的字节数的长度。

我现在知道,在128,我们从-128开始向后计数。 我想知道的是按位运算在这里是如何工作的 – 更具体地说,我是如何得到负数的二进制表示。

如果我理解按位&正确,则结果等于只设置两个数字的公共位的数字。 假设byte b = -128 ,我们将:

 b & 0xff // 128 1000 0000-128 1111 1111 255 --------- 1000 0000 128 

那我怎么能到达1000 0000 -128? 如何获得像-72或-64这样不太明显的二进制表示?

为了获得负数的二进制表示,你计算两个补码:

  • 获取正数的二进制表示
  • 反转所有位
  • 添加一个

我们以-72为例:

 0100 1000 72 1011 0111 All bits inverted 1011 1000 Add one 

所以-72的二进制(8位)表示是10111000

实际发生的是以下内容:您的文件有一个值为10111000的字节。 当解释为无符号字节(可能是你想要的)时,这是88。

在Java中,当此字节用作int时(例如,因为read()返回int,或者因为隐式提升),它将被解释为带符号的字节,并且符号扩展为11111111 11111111 11111111 10111000 。 这是一个值为-72的整数。

通过与0xff 00000000 00000000 00000000 10111000 ,您只保留最低的8位,因此您的整数现在为00000000 00000000 00000000 10111000 ,即88。

我想知道的是按位运算在这里是如何工作的 – 更具体地说,我是如何得到负数的二进制表示。

负数的二进制表示是对应的正数进行比特翻转,其中加1。 这种表示称为二进制补码 。

我想这里的魔力是字节存储在一个更大的容器中,可能是32位int。 如果该字节被解释为有符号字节,则它将被扩展为表示32位int中的相同数字,即如果该字节的最高有效位(第一个)是1,那么在32位int中该1左边的位也变为1(这是由于负数表示的方式,两个补码)。

现在,如果你& 0xFF那个int你切断了那些1并最终得到一个表示你读过的字节值的“正”int。

不确定你真正想要的:)我假设你问的是如何提取有符号的多字节值? 首先,看看签名扩展单个字节时会发生什么:

 byte[] b = new byte[] { -128 }; int i = b[0]; System.out.println(i); // prints -128! 

因此,符号正确地扩展到32位而不做任何特殊操作。 字节1000 0000正确扩展到1111 1111 1111 1111 1111 1111 1000 0000.您已经知道如何通过AND与0xFF来抑制符号扩展 – 对于多字节值,您只需要对最高有效字节的符号进行扩展,您希望将其视为无符号的较低有效字节(示例假设网络字节顺序为16位int值):

 byte[] b = new byte[] { -128, 1 }; // 0x80, 0x01 int i = (b[0] << 8) | (b[1] & 0xFF); System.out.println(i); // prints -32767! System.out.println(Integer.toHexString(i)); // prints ffff8001 

您需要抑制除最重要字节之外的每个字节的符号扩展,因此要将带符号的32位int提取为64位长:

 byte[] b = new byte[] { -54, -2, -70, -66 }; // 0xca, 0xfe, 0xba, 0xbe long l = ( b[0] << 24) | ((b[1] & 0xFF) << 16) | ((b[2] & 0xFF) << 8) | ((b[3] & 0xFF) ); System.out.println(l); // prints -889275714 System.out.println(Long.toHexString(l)); // prints ffffffffcafebabe 

注意:在基于intel的系统上,字节通常以相反的顺序存储(最低有效字节优先),因为x86架构在内存中按此顺序存储较大的实体。 很多x86发起的软件也以文件格式使用它。

要获得无符号字节值,您也可以。

 int u = b & 0xFF; 

要么

 int u = b < 0 ? b + 256 : b; 

对于第7位设置的字节:

 unsigned_value = signed_value + 256 

在使用字节计算数学时,您计算模256.有符号和无符号之间的区别在于您为等价类选择不同的代表,而作为位模式的基础表示对于每个等价类保持相同。 这也解释了为什么加法,减法和乘法与位模式具有相同的结果,无论您是使用有符号还是无符号整数进行计算。