在Java中将字节转换为整数

58

我已经生成了一个安全的随机数,并将其值存储在字节中。以下是我的代码。

SecureRandom ranGen = new SecureRandom();
byte[] rno = new byte[4]; 
ranGen.nextBytes(rno);
int i = rno[0].intValue();

不过,我遇到了一个错误:

 byte cannot be dereferenced

1
字节(byte)和 Byte 有区别。后者是一个对象,支持 .intValue() 方法。前者是原始的字节值,可以像 int i = rno[0] 这样进行赋值。 - wuppi
1
请参见https://dev59.com/TXE95IYBdhLWcg3wRLwt - DNA
7个回答

104

你的数组是 byte 基本类型,但你正在尝试调用它们的方法。

你不需要进行任何显式转换将 byte 转换为 int,只需:

int i=rno[0];

...因为它不是一个沮丧的情况。

请注意,默认情况下,byteint的转换行为是保留值的符号(请记住,在Java中,byte是有符号类型)。所以例如:

byte b1 = -100;
int i1 = b1;
System.out.println(i1); // -100

如果你认为 byte 是无符号的(156),而不是有符号的(-100),从Java 8开始,有 Byte.toUnsignedInt 方法可以使用:
byte b2 = -100; // Or `= (byte)156;`
int i2 = Byte.toUnsignedInt(b2);
System.out.println(i2); // 156

在Java 8之前,要获取等效的int值,您需要屏蔽掉符号位。
byte b2 = -100; // Or `= (byte)156;`
int i2 = (b2 & 0xFF);
System.out.println(i2); // 156

仅供完整性 #1:如果您确实希望出于某种原因使用Byte的各种方法(在此您不需要),您可以使用装箱转换

Byte b = rno[0]; // Boxing conversion converts `byte` to `Byte`
int i = b.intValue();

或者Byte构造函数
Byte b = new Byte(rno[0]);
int i = b.intValue();

但是再说一次,在这里你不需要那个。
只是为了完整性 #2:如果它是一个向下转型(例如,如果你试图将一个 int 转换为 byte),你只需要使用一个强制类型转换即可:
int i;
byte b;

i = 5;
b = (byte)i;

这样可以向编译器保证你知道这是一个向下转型,从而避免出现“可能丢失精度”的错误。

使用Byte构造函数不是一个好主意。只需使用Byte b = rno[0];即可。 - xehpuk
@xehpuk:我应该提到了强制类型转换,现在已经完成了。 - T.J. Crowder
1
为什么在这种情况下装箱转换优于Byte构造函数?据我理解,最终装箱转换还是会使用Byte构造函数,我的理解有误吗? - user1
1
@user3772575:是的。为了避免为该字节值分配新实例,装箱将使用Byte.valueOf而不是new Byte(); 更多信息请参见此处。(规范实际上并没有说,但它确实这样做。您可以通过查看字节码来确定。) - T.J. Crowder
1
据我最后一次检查,在Java 8中,Byte类提供了一个名为toUnsignedInt(byte)的静态方法。这应该有助于更轻松地进行转换并使代码更易读。 - yolob 21
@yolob21 - 谢谢。是的,这个答案是在 Java 8 发布前两年就出现了。 :-) - T.J. Crowder

48
byte b = (byte)0xC8;
int v1 = b;       // v1 is -56 (0xFFFFFFC8)
int v2 = b & 0xFF // v2 is 200 (0x000000C8)

大多数情况下,v2 是你真正需要的方式。


是因为32位机器生成了长整型数0xffffffc8,谢谢。 - herbertD
2
这是正确的答案,而且v2几乎总是你想要的。在Java中,将byte隐式转换为int而没有使用& 0xff会导致错误。 - Luke Hutchison
1
这并不是由于 Java 的隐式转换中存在“漏洞”,而是由于开发人员的误解。Java 的 byte 原始类型只支持 -128 到 127 之间的数值。如果有人试图将无符号值(如 0xC8)塞入其中,它可能会以一堆前导 1 进行翻转(因为采用了二进制补码表示法)。同样地,在此问题中,整个位空间可以设置符号位,并且数值提升也会执行类似操作。 - ingyhere
Java在幕后实际上正在进行数字转换,根据JLS的规定,这意味着两个操作数都会被转换为int原语。 - ingyhere
不是Java中的错误,而是用户编写的Java代码中的错误 :) 大多数程序员没有意识到在Java中字节是带符号的,坦白地说,这是一种愚蠢的默认行为,他们将字节视为Java规范中的有符号数,但出于与其他整数类型的一致性考虑,它们采用了这种方式,因为它们中没有任何无符号版本。当将short转换为int时也会遇到同样的问题,因为人们通常也没有意识到short是有符号的。(由于其非常有限的值范围,使用byte更容易触发符号扩展错误。)但是人们确实期望int和long是带符号的。 - Luke Hutchison

36

如果你想将这4个字节组合成一个整数,你需要执行以下操作:

int i= (rno[0]<<24)&0xff000000|
       (rno[1]<<16)&0x00ff0000|
       (rno[2]<< 8)&0x0000ff00|
       (rno[3]<< 0)&0x000000ff;

我使用3个特殊运算符:|是按位逻辑或,&是逻辑与,<<是左移。

实质上,我通过将字节就地移动并对它们进行OR运算来将4个8位字节组合成单个32位整数。

我还使用& 0xff确保任何符号提升都不会影响结果。


4
我会执行 (rno[0]&0x000000ff)<<24|(rno[1]&0x000000ff)<<16| ... 等操作,这样我可以将 AND 操作的操作数变成一个常量。另外,你也可以直接使用 ((int)rno[0])<<24|((int)rno[1])<<16| ...,因为类型转换会自动将字节值放入基本 int 类型空间中。 - ingyhere
1
@ingyhere,你的“Alternatively”建议是不正确的!请看Sheng.W的回答。你几乎总是需要执行(b & 0xff)将一个字节转换为整数,而不是((int)b)或等效的隐式转换,因为存在符号扩展问题。 - Luke Hutchison
你抓住我了 @LukeHutchison ... 我评论中的第二个仅适用于 Java byte 原始类型范围内(最高达 127)的无符号值(正空间)。 - ingyhere
有符号值的正范围并不是无符号值,它仍然带有'+'的符号。在Java中,字节、短整型、整数和长整型都是带符号的,没有无符号版本! - Luke Hutchison

8

Java中的原始数据类型(如byte)没有方法,但您可以直接执行以下操作:

int i=rno[0];

6
不是Byte,而是Byte.intValueByte是一个类,而byte是一种基本数据类型。 - MByD
这个答案不完整。请参考Sheng.W的回答。你几乎总是需要执行(b & 0xff)byte转换为int,而不是使用((int) b)或等效的隐式转换int i = b;,因为对于高位设置的字节,存在符号扩展的问题。忘记执行此操作是错误的常见来源。 - Luke Hutchison

4
字节会自动转换为整数。
只需说:
int i= rno[0];

9
请注意,符号位被保留了,这并不总是您想要的(Java没有无符号字节)。 - DNA

4
我认为应该是:
byte b = (byte)255;
int i = b &255;

-2

int b = Byte.toUnsignedInt(a);

将a转换为无符号整数并赋值给b。

2
目前你的回答不够清晰,请[编辑]以添加更多细节,帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何编写好答案的更多信息。 - Community

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