Java中的无符号字节

17

Java中的字节默认为有符号。我在其他帖子上看到一个方法可以使用以下代码来获得无符号字节:int num = (int) bite & 0xFF

请问能否有人解释一下为什么这个操作可以将有符号字节转换为无符号字节,然后再转换为相应的整数?用11111111进行按位与运算应该会得到相同的字节吧?

5个回答

27

类型转换的优先级高于&操作符。因此,您首先将其强制转换为int,然后进行AND运算以屏蔽掉所有设置的高位比特,包括Java使用的二进制补码符号位,留下原始字节的正值。例如:

let byte x = 11111111 = -1
then (int) x = 11111111 11111111 11111111 11111111
and x & 0xFF = 00000000 00000000 00000000 11111111 = 255

而您已经有效地从原始字节中删除了符号。


1
嗯...但是如果我在强制转换之前强制执行&,我仍然会得到239System.out.println((int) (((byte) -17) & 0xFF)); - aioobe
3
这里实际上不需要进行类型转换,因为0xFF是一个整数字面值(没有字节的字面值)。 - Michael Borgwardt
所以,无符号的 11111111 是 255。然而,当它被视为有符号数时,它是 -1。实际上,你的代码将其解释为无符号数,而不是有符号数(这是 Java 的默认设置)? - darksky
@Nayefc:是的,通过将其更改为int但保持完全相同的位模式,我们可以实质上使用有符号int来表示无符号字节。但它仍然使用与int一样多的存储空间。 - Ryan Stewart
@aioobe:正如Michael所指出的那样,0xFF是一个int类型,因此您的byte会被提升为int,结果是相同的。之后,-17的二进制补码与反码中的238相同(在9位或更多位中)。 - Ryan Stewart
@RyanStewart 为了节省存储空间,你可以考虑使用 short 而不是 int。 - Bovaz

16

把一个字节与11111111进行AND运算会得到相同的字节,对吗?

但实际上你是在与00000000000000000000000011111111进行AND运算,因为0xFF是一个int字面量,在Java中没有byte字面量。所以,这个byte会被提升为int类型(强制类型转换是不必要的),然后它的符号被扩展了(即保留了byte的可能负值),但是通过与所有这些零进行AND运算,符号扩展被还原了。结果是一个int,其最低有效位恰好是原来的byte,因此它的值就像byte在无符号情况下的值一样。


1
+1,你做得很好... 据我所知,& 0xFF 抛弃了“符号扩展”的位。可能值得一提... - aioobe

12

在Java 8中,Byte类出现了这样的方法:

/**
 * Converts the argument to an {@code int} by an unsigned
 * conversion.  In an unsigned conversion to an {@code int}, the
 * high-order 24 bits of the {@code int} are zero and the
 * low-order 8 bits are equal to the bits of the {@code byte} argument.
 *
 * Consequently, zero and positive {@code byte} values are mapped
 * to a numerically equal {@code int} value and negative {@code
 * byte} values are mapped to an {@code int} value equal to the
 * input plus 2<sup>8</sup>.
 *
 * @param  x the value to convert to an unsigned {@code int}
 * @return the argument converted to {@code int} by an unsigned
 *         conversion
 * @since 1.8
 */
public static int toUnsignedInt(byte x) {
    return ((int) x) & 0xff;
}

1
这并没有回答问题。 - gebuh
1
@gebuh JavaDoc 的第一个段落回答了这个问题,但没有像被接受的答案那样明确地(使用实际二进制数)表述。 - TWiStErRob
1
是的,这确实回答了问题,只是有点难理解为什么。 - Nicholas DiPiazza

3

如您所见,结果是一个int而不是一个字节。

它是如何工作的呢?假设我们有一个byte b = -128;,这被表示为1000 0000,那么当您执行此行时会发生什么?让我们使用一个临时的int来解释一下,比如:
int i1 = (int)b; 现在i1是-128,实际上它的二进制表示如下:

1111 1111 1111 1111 1111 1111 1000 0000

i1 & 0xFF在二进制下是什么样的呢?

1111 1111 1111 1111 1111 1111 1000 0000
&
0000 0000 0000 0000 0000 0000 1111 1111

这会导致
0000 0000 0000 0000 0000 0000 1000 0000

这个值恰好是128,意味着您的有符号值转换为无符号值。

编辑
将字节-128 .. 127转换为0 .. 255

int unsignedByte = 128 + yourByte;

使用一个字节无法表示128到255之间的值,您必须使用其他类型,例如int或smallint。


好的!明白了。那么如何将有符号字节转换为无符号字节呢?我正在存储字节,在Java中它们被解释为有符号的。我该如何获得该字节的无符号值?我需要让我的字节表示0-255,而不是-128->+127。 - darksky

0

是的,但这样你可以确保你永远不会得到一个大于255或小于0的数字。

如果第一位是1,则该数字为负数。如果将字节转换为整数,如果它是负数,则会在前面添加1个字节,如果是正数,则添加0个字节。运行and例程将删除第一个8左侧的所有字节。这实际上会将256添加到负字节。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接