观前提示:本文涉及到二进制数据的相关知识,博主推荐没有学习过二进制的同学可以去看一下《程序是怎样跑起来的》一书中有关二进制的知识。
首先我们来看一下int和byte数组互相转换的两个函数。
//int转Byte数组
public byte[] getByte(int number){
byte[]bt=new byte[4];
bt[0]=(byte) ((number>>0) & 0xff);
bt[1]=(byte) ((number>>8) & 0xff);
bt[2]=(byte) ((number>>16) & 0xff);
bt[3]=(byte) ((number>>24) & 0xff);
return bt;
}
//byte数组还原为int
public int getInt(byte[]bt){
int number=(bt[3]& 0xff)<<24|
(bt[2]& 0xff)<<16|
(bt[1]& 0xff)<<8|
(bt[0]& 0xff)<<0;
return number;
}
直接将int强制转型为byte会发生什么?
我们可以试着运行一下下面的代码:
public void test(){
int a=10;
byte a1=(byte)a;
System.out.println(a1);
int b=127;
byte b1=(byte)b;
System.out.println(b1);
int c=128;
byte c1=(byte)c;
System.out.println(c1);
int d=500;
byte d1=(byte)d;
System.out.println(d1);
}
输出为:
可以发现,当我们的int转换成byte,有时可以正常转换,但有时却不能得到正确的数值。为什么会这样子?
需要知道的一些知识
byte是最小的基本单位,它的长度是8个bit(比特),也就是8位。所以说1个byte可以用来表示2^8=256个数字。
其他的数据类型长度都是byte的整数倍。
一个int的长度是4个byte,也就是32位,那么如果我们想要将一个int转换为byte,起码需要使用4个byte数据。比如说在前面的例子中,将10、127、128和500分别表示为32位二进制(因为int是32位二进制):
当我们将int强制转型为byte时,由于byte只能装下8bit的数据,所以byte只会保存上图中红色部分的8bit数据(也就是int数据的低八位)。所以说10和127强制转型前后都没有变,而128强制转型后符号位(二进制最高位为符号位)从0变成了1,500强制转型后不仅符号位改变,而且还丢失了一位数据。
怎么样把int数据完整的保留下来
如果我们想要把int数据完整的保存下来也很简单,只需要像剪纸片一样把一个int数据剪成均等的4份,将每一份分别存到一个byte里面去不就好了。但是由于byte每次只能接收低八位的数据,所以我们每收集一次,就要将int右移8位。
当我们需要将byte数组还原为int时,只需要将byte值再左移相应的位数,然后再组合到一起就可以了。至于怎么组合在一起……就是考验技术的时候了!
如果不用&0xff,byte移位后直接组合到一起会发生什么呢?
试着运行一下下面这个代码:
public void test(){
//我们试着把-2002247180这个数转为byte,再还原为int
int a=-2002247180;
byte[]b=getByte(a);
int c=getInt(b);
System.out.println(c);
}
//下面是两个byte和int转换的方法,与文章最开始不同的是下面两个方法没有使用&0xff
public byte[] getByte(int number){
byte[]bt=new byte[4];
bt[0]=(byte) (number>>0);
bt[1]=(byte) (number>>8);
bt[2]=(byte) (number>>16);
bt[3]=(byte) (number>>24);
return bt;
}
public int getInt(byte[]bt){
int number=(bt[3])<<24|
(bt[2])<<16|
(bt[1])<<8|
(bt[0])<<0;
return number;
}
结果是:
这期间发生了什么?
让我们一行一行地分析test()函数:
第一行创建了一个int值a为-2002247180。
这个数用二进制表示出来是:
第二行调用了getByte函数,执行了我们前面说到的使用移位,再逐次取低八位放入byte的操作。
第三行调用了getInt函数,符号“|”代表或运算,意思是两个二进制数对应位数只要有一个为1,结果就为1。
int number=(bt[3])<<24|(bt[2])<<16|(bt[1])<<8|(bt[0])<<0;
注意,这里因为在式子的左边是int,所以右边的8位的byte全部都自动转型为32位的int,高位补全符号位。进行左移操作时,右边补0。
第四行输出了还原后得到的int c,结果为-12。因为受到了bt[0]左边补全的1的影响,进行与运算后高24位全部都变为了1,导致最后输出了错误的结果。
把a的值定为2139062132,再运行一下试试?好像又没有出错了?
&0xff的含义
0xff是一个十六进制数,用32位二进制可以表示为00000000 00000000 00000000 11111111。符号“&”代表与运算,意思是两个二进制数对应位数都为1时,结果才为1,比如说1001&0111的结果为0001。所以说&0xff就像一把剪刀,它只保留一个二进制数的低八位,其余的位数全部置0。
如果在前面的例子中我们使用了&0xff,也就是剪掉了多余的1,只保留原始的byte值的部分,再进行与运算就可以得到正确的int了。
最后
想要弄明白为什么int和byte数组互相转换时为什么要用&0xff,最好的方法就是自己试验一下。&0xff的作用是什么?使用&0xff和不使用有什么不同?若不使用&0xff,什么情况不能正确转换?什么时候能够正确转换?
博主在这里说一下自己试验的结论,当然你也可以通过自己的方法试验出来:
- int转byte数组时不需要用到&0xff,因为int转byte时,系统会自动将溢出的位数忽略。
- byte数组还原为int时,低24位截取出的3个byte符号位都为0时,不论是否使用&0xff都不会影响正常转换:
比如说前面提到的2139062132,不论是否使用&0xff都可以成功转换
-8421516高八位的byte的符号位为1,但是其余三个byte的符号位都为0,不论是否使用&0xff都可以成功转换 - byte数组还原为int时,如果使用的int值拆分成的4个byte符号位中,低24位截取出的3个byte只要有一个符号位为1,只有使用&0xff才能正常转换:
像-100696204、-105153036这些数,byte转为int时必须使用&0xff,否则就会出错。
这是博主的第一篇“试验型”博客,自己发现问题,搜集资料,进行代码试验,思考得出结论。所以说可能会有一些纰漏或者描述不严谨的地方,望各路大神批评指正!