引言
昨天有个朋友问我long转byte要怎么转,然后吃饭期间我问了身边的人,发现也不会转。所以我想有必要写一些位操作符和转化规则,同时还有一些不常用,但是源码中经常出现的操作符。不可能所有的都写出来,但是举一反三就可以了。在技术点中介绍一些不常用但是常见的操作符,后半部分展示实现转化的代码。笔者目前整理的一些blog针对面试都是超高频出现的。大家可以点击链接:http://blog.csdn.net/u012403290
技术点:
计算机中存储是用补码!!,同时注意一下计算省略了高位不变部分
1、byte:有符号(意思是有正和负),在网络传输中都是会用到byte的,它占1个字节,共8位,比如说11111111就可以用1个byte表示,转化为10进制:- (2的6次+2的5次+2的4次+2的3次+2的2次+2的1次+2的0次) = -127。其中前7位表示数字,最高位表示符号,0为正,1为负。范围是 (-2的7次 ~ 2的7次 - 1),那为什么前面最小是-127,范围最小又是-128呢?因为规定-0(10000000)为-128。
2、short:有符号,占2个字节,共16位。同byte一样,它的取值范围就是 (-2的15次 ~ 2的15次 - 1)。
3、int :有符号,占4个字节,共32位。它的取值范围就是(-2的31次 ~ 2的31次)。
4、long:有符号,占8个字节,共64位,它的取值范围就是(-2的63次 ~ 2的63次)。
5、^:表示异或位运算,两者相同则为0,两者不同则为1。比如说15^2,15用二进制表示就是1111,2用2进制表示就是0010,两者进行异或运算,结果就是1101,转换为十进制就是13。
A | B | A^B |
---|---|---|
1 | 1 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
6、|:表示或运算,两者只有有一个为1就为1, 比如说13|2,13用二进制表示就是1101,2用二进制表示就是0010,两者进行或运算,那么结果就是1111,转换为十进制就是15。
A | B | A或B |
---|---|---|
1 | 1 | 1 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
7、&:表示与运算,两者都为1就为1,其余都为0,比如说15&2, 13用二进制表示就是1111,2用二进制表示就是0010, 两者进行与运算,那么结果就是0010,转换为十进制就是2。
A | B | A&B |
---|---|---|
1 | 1 | 1 |
1 | 0 | 0 |
0 | 1 | 0 |
0 | 0 | 0 |
8、~:取反,就是本来是0变成1,本来是1变成0。
A | ~B |
---|---|
1 | 0 |
0 | 1 |
注意:本来我打算不想解释这块的,但是学知识,既然要学了,就尽自己最大努力一次性一个知识点学透彻。我举个例子,比如说-15|3等于多少呢?有些人会觉得-15转化为二进制就是10001111,而3转化为二进制就是00000011,那么结果应该是10001111呀,转换为十进制就是-15呀?大家可以自己写个demo就会发现是不对的。要注意在计算机中所有的都是用补码的形式存储的,之所以上面介绍两个正数是对的,因为正数的反码和补码都是一样的。而负数求补码应该是出去符号位取反+1,我们再来看看这个题-15|3,其中-15的原码为10001111,反码为11110000,那么补码就是11110001,然后3的补码为00000011,两者进行或操作就是11110011,你以为结束了么?还没有,再又要求它的原码了,原码就是补码再求补码再+1(是不是已经晕掉了?),也就是10001101,结果就是-13。为了大家都好理解,我这里用算式整理一下:
求 -15|3
[-15]原码 = 10001111
[-15]反码 = 11110000 //原码求反码符号位不变
[-15]补码 = 11110001 //反码+1等于补码
[3]原码 = 00000011
[3]反码 = 00000011 //正数都一致
[3]补码 = 00000011 //正数都一致
-15|3 = 11110011 //两个补码进行或操作
[结果]补码 = 11110011 //上面求得的值
[结果]反码 = 10001100 //符号位不变
[结果]原码 = 10001101 //反码+1
100001101 转化为十进制就是-13。
不知道我这么解释会不会更加清楚一些呢?同理的,上面那些(尤其是求反‘~’我故意没写例子,大家自己去试试吧)。
在说一次,正数的原码,补码,反码都一样,如果一定要说为什么一样,我就举个例子。有这么一个等式7+(-7)=0。
我们知道
[-7]原 = 10000111
[-7]反 = 11111000
[-7]补 = 11111001
那么如果要存在一个值和[-7]补码相加等于0,是不是就是00000111!!所以正数的补码和原码是一致的。这样解释虽然怪怪的,但是可以知道的确是这样的。
9、<<:左移运算符,不需要考虑符号位,因为在后面补0,把二进制的数字向左移动,低位补0。比如说 3<<2。3的二进制码为11,向左移动2位就是1100,那么结果就是12。
10、>>:右移运算符,带符号位。根据这个要移动的数字决定正负,如果是正数,那么就在前面补0,如果是负数就在前面补1。比如说 3>>2,3的二进制码为00000011,向右移动2位,就变成00000000,转化为十进制就是0了(3是正数所以在高位补0)。再比如说-3>>2,-3的二进制码为10000011,转化为补码(只要涉及到负数,就需要转换到补码计算,正数之所以不用转化,是因为他们的补码和原码是一致的)11111101,进行位移运算,就是11111111,这个是补码,需要再转回原码。那么就是取反+1,结果就是10000001,转化为十进制就是-1。
11、>>>:右移运算符,与上面的区别就是这个运算符是无符号的。不论正负,高位都补0。如果要用-3>>2来验证,因为高位补0,就需要把所有的位数都写全了。如果是int,那就要写成32位来运算。切记切记。上面很多因为高位的变化“取反再取反”会导致前面所有的位都没变所以我就简化了,但是这个>>>运算符不行哦,它会把负数变为正数。
代码实现
基本步骤:
①分析转化和代转化之间位数的关系
②计算偏移量。每次都取最后8位
③把最后8位和0xff进行&操作
④得出结果。
有人会问,为什么要和0xff进行&操作?这里我做一下解释,因为再在割的时候,我们要保证最后8位是一致的,更高位都要置0,这样才能保证数据的一致性。比如说由byte转化成short。位数从8位变成了16位,那么在计算机自己看来,它会把前面多出来的8位置1。而&0xff就可以把前面所有的位数都置0。
1、short 与 byte互相转化
public static byte[] short2byte(short s){
byte[] b = new byte[2];
for(int i = 0; i < 2; i++){
int offset = 16 - (i+1)*8; //因为byte占4个字节,所以要计算偏移量
b[i] = (byte)((s >> offset)&0xff); //把16位分为2个8位进行分别存储
}
return b;
}
public static short byte2short(byte[] b){
short l = 0;
for (int i = 0; i < 2; i++) {
l<<=8; //<<=和我们的 +=是一样的,意思就是 l = l << 8
l |= (b[i] & 0xff); //和上面也是一样的 l = l | (b[i]&0xff)
}
return l;
}
2、int和byte的转化也差不多
public static byte[] int2byte(int s){
byte[] b = new byte[2];
for(int i = 0; i < 4; i++){
int offset = 16 - (i+1)*8; //因为byte占4个字节,所以要计算偏移量
b[i] = (byte)((s >> offset)&0xff); //把32位分为4个8位进行分别存储
}
return b;
}
public static int byte2int(byte[] b){
int l = 0;
for (int i = 0; i < 4; i++) {
l<<=8; //<<=和我们的 +=是一样的,意思就是 l = l << 8
l |= (b[i] & 0xff); //和上面也是一样的 l = l | (b[i]&0xff)
}
return l;
}
那么long和byte的转化,甚至是long和short等之间的转化大家自己琢磨。看到这里是不是觉得自己要学的东西很多,大学学过的都忘记了呢?没关系,以后我会经常给大家复习一些重要但是不常用的东西哦。