观前提示:本文涉及到二进制数据的相关知识,博主推荐没有学习过二进制的同学可以去看一下《程序是怎样跑起来的》一书中有关二进制的知识。

首先我们来看一下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);
}

输出为:

javabyte数组清零 java new byte数组_数据


可以发现,当我们的int转换成byte,有时可以正常转换,但有时却不能得到正确的数值。为什么会这样子?

需要知道的一些知识

byte是最小的基本单位,它的长度是8个bit(比特),也就是8位。所以说1个byte可以用来表示2^8=256个数字。

其他的数据类型长度都是byte的整数倍

javabyte数组清零 java new byte数组_java_02


一个int的长度是4个byte,也就是32位,那么如果我们想要将一个int转换为byte,起码需要使用4个byte数据。比如说在前面的例子中,将10、127、128和500分别表示为32位二进制(因为int是32位二进制):

javabyte数组清零 java new byte数组_java_03


当我们将int强制转型为byte时,由于byte只能装下8bit的数据,所以byte只会保存上图中红色部分的8bit数据(也就是int数据的低八位)。所以说10和127强制转型前后都没有变,而128强制转型后符号位(二进制最高位为符号位)从0变成了1,500强制转型后不仅符号位改变,而且还丢失了一位数据。

怎么样把int数据完整的保留下来

如果我们想要把int数据完整的保存下来也很简单,只需要像剪纸片一样把一个int数据剪成均等的4份,将每一份分别存到一个byte里面去不就好了。但是由于byte每次只能接收低八位的数据,所以我们每收集一次,就要将int右移8位

javabyte数组清零 java new byte数组_数组_04


当我们需要将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;
}

结果是:

javabyte数组清零 java new byte数组_javabyte数组清零_05

这期间发生了什么?

让我们一行一行地分析test()函数:
第一行创建了一个int值a为-2002247180。

这个数用二进制表示出来是:

javabyte数组清零 java new byte数组_javabyte数组清零_06


第二行调用了getByte函数,执行了我们前面说到的使用移位,再逐次取低八位放入byte的操作。

javabyte数组清零 java new byte数组_数组_07


第三行调用了getInt函数,符号“|”代表或运算,意思是两个二进制数对应位数只要有一个为1,结果就为1。

int number=(bt[3])<<24|(bt[2])<<16|(bt[1])<<8|(bt[0])<<0;

注意,这里因为在式子的左边是int,所以右边的8位的byte全部都自动转型为32位的int,高位补全符号位。进行左移操作时,右边补0。

javabyte数组清零 java new byte数组_数据类型_08

第四行输出了还原后得到的int c,结果为-12。因为受到了bt[0]左边补全的1的影响,进行与运算后高24位全部都变为了1,导致最后输出了错误的结果。

把a的值定为2139062132,再运行一下试试?好像又没有出错了?

&0xff的含义

0xff是一个十六进制数,用32位二进制可以表示为00000000 00000000 00000000 11111111。符号“&”代表与运算,意思是两个二进制数对应位数都为1时,结果才为1,比如说1001&0111的结果为0001。所以说&0xff就像一把剪刀,它只保留一个二进制数的低八位,其余的位数全部置0。

javabyte数组清零 java new byte数组_java_09

如果在前面的例子中我们使用了&0xff,也就是剪掉了多余的1,只保留原始的byte值的部分,再进行与运算就可以得到正确的int了。

javabyte数组清零 java new byte数组_数组_10

最后

想要弄明白为什么int和byte数组互相转换时为什么要用&0xff,最好的方法就是自己试验一下。&0xff的作用是什么?使用&0xff和不使用有什么不同?若不使用&0xff,什么情况不能正确转换什么时候能够正确转换?

博主在这里说一下自己试验的结论,当然你也可以通过自己的方法试验出来:

  1. int转byte数组时不需要用到&0xff,因为int转byte时,系统会自动将溢出的位数忽略。
  2. byte数组还原为int时,低24位截取出的3个byte符号位都为0时,不论是否使用&0xff都不会影响正常转换:
    比如说前面提到的2139062132,不论是否使用&0xff都可以成功转换

    -8421516高八位的byte的符号位为1,但是其余三个byte的符号位都为0,不论是否使用&0xff都可以成功转换
  3. byte数组还原为int时,如果使用的int值拆分成的4个byte符号位中,低24位截取出的3个byte只要有一个符号位为1,只有使用&0xff才能正常转换:


    像-100696204、-105153036这些数,byte转为int时必须使用&0xff,否则就会出错。

这是博主的第一篇“试验型”博客,自己发现问题,搜集资料,进行代码试验,思考得出结论。所以说可能会有一些纰漏或者描述不严谨的地方,望各路大神批评指正!