珍惜作者劳动成果,如需转载,请注明出处。
http://blog.youkuaiyun.com/zhengzechuan91/article/details/50476882
写这篇博客源于在进行长连接通信的时候我们需要将流数据和我们的String、基本类型的数据之间进行转换,我们知道byte[]与String之间的转换相当方便,那么接下来我们就要弄懂byte[]与基本数据类型之间的转换了。
计算机中的存储
首先,本文讲的东西并不局限于Java,每种语言虽然基本数据类型的长度不同,但是计算机存储的原理都是一样的。我们知道,不管是什么语言,数据都有” 原码”,” 反码”,” 补码”三种形式,”原码”是我们能看懂的编码方式,而为了解决使用原码带来的一些问题,计算机内部采用”补码”的形式存储数据。先来说说”补码”存储的原理:
1.如果是正数,补码与原码相同;
2.如果是负数,补码则是先对原码的绝对值按位取反,再加1。
我们看两个例子:
10: 原码: 00001010
反码: 00001010
补码: 00001010
-10:原码:10001010
反码: 11110101
补码: 11110110
所以十进制10在计算机中的存储格式为00001010,而十进制-10的存储格式为11110110。
不同格式的转换
规则1:少位数 —> 多位数:我们在进行数据类型转换时,对数据做补齐的操作,保证数据位数发生改变时其值依然不变。正数的在其左边补0,负数则是在其左边补1,而无符号的则也是在其左边补0。
规则2:多位数 —> 少位数:如果我们需要想将多位数转换为少位数,则只需对数据拦截低位数的长度即可。
来看个例子:
byte b = (byte) -1;
char c = (char) b;
int i = (int) c;
int(32位) –> byte(8位):-1默认为32位的int类型,存储为0xffffffff,转换为8位的byte类型则执行规则2拦截低8位,所以b存储类型为的0xff,即十进制的-1;
byte(8位) –> char(16位):byte为8位,转换为char类型时执行规则1,在其左边补齐8个1,则c存储数据为0xffff,而char为无符号的16位,十进制对应的是65535;
char(16位) –> int(32位):char为16位的无符号类型,执行规则1,向左边补齐16个0,则i的值为0x0000ffff,对应十进制的65535。
移位规则
- 当发生左移(<<)时,右边补0;
- 当发生有符号的右移(>>)时,左边补符号位,但是符号位不变。
- 当发生无符号的右移(>>>)时,无论正负,左边补0。
int与byte[]转换
有了上面的基础我们就可以看看在长连接通信时最为常见的int与byte[]之间是怎么转换的。
public static byte[] int2byte(int res) {
byte[] targets = new byte[4];
// 最低位
targets[0] = (byte) (res & 0xff);
// 次低位
targets[1] = (byte) ((res >> 8) & 0xff);
// 次高位
targets[2] = (byte) ((res >> 16) & 0xff);
// 最高位,无符号右移。
targets[3] = (byte) (res >>> 24);
return targets;
}
上面的方法是将一个int类型的数据存储在了一个大小为4的byte[]里,由于int类型为32位,而byte为8位,则需要32 / 8 = 4个byte单元来存储。
我们看到好多都与0xff进行了与操作,其实这个与操作在位数不同转化时尤为有效,我们来分析一下上面的代码。
targets[0]是将32位的int型与0xff进行与运算时,编译器会自动将0xff扩展到32位0x000000ff,这样与操作之后高24位全部清零,只留下了低8位,然后转化为byte时,刚好只拦截了低8位。后面3个都是同理,只是先将要拦截的那8位移到了低8位。值得一提的是,有符号右移(>>)时补齐的是符号位,而无符号右移(>>>)时补齐的则直接是0。
上面可能有疑惑:0xff扩展到32位为什么不是0xffffffff呢?我们来看看下面的定义:
int a = 0xff;
int b = 0xffffffff;
我们打印a和b的值我们发现,a的十进制位255,b的十进制为-1。a存储的是正数,当然是0补齐啦。
接下来我们再看看将byte[]转换为int的方法
public static int byte2int(byte[] res) {
int targets = (res[0] & 0xff) | ((res[1] << 8) & 0xff00)
| ((res[2] << 24) >>> 8) | (res[3] << 24);
return targets;
}
res[0]与0xff进行与运算,首先编译器会将res[0]转化为int类型,而0xff会被补齐为32位,即0x000000ff,与0xff进行与运算后则虽已为32位,但是只有低8位数字有效,其余全部被置零。
res[1]则是先左移8位,使res[1]的数据移到次低8位,在与0xff00进行与运算使次低8位有效,其余清零。
res[2]则是先左移24位,使res[2]置于高8位,其余位置零,再无符号右移,使次8位有效。
res[3]则是直接左移24位,高8位有效,其余置零。
最后进行或运算,将不同的byte单元合并到int型中来。
理解了上面说的这么多,我们再也不必担心繁杂的逻辑运算了。