在实际开发中我们一般不常用到java的位运算,但有时去看java的一些源码又经常会看到这些位运算符,所以有必要了解一下位运算,同时位运算的使用对运算效率会有一定的提升,特别是高重复运算,会节约相当的时间开支。
Java位运算符列表如下: | ||
& | 与 | 同为1则为1,其余为0 |
| | 或 | 一个为1则为1,否则为0 |
^ | 异或 | 相同为0,不同为1 |
~ | 非 | 为1则为0,为0则为1 |
<< | 左位移 | 将运算符左边的运算对象向左移动运算符右侧指定的位数(在低位补0) |
>> | 右位移 | 将运算符左边的运算对象向右移动运算符右侧指定的位数。 “有符号”右移位运算符使用了“符号扩展”:若值为正,则在高位插入0;若值为负,则在高位插入1。 |
>>> | 无符号右移 | 无论正负,都在高位插入0 |
在了解位运算之前,先要了解一下二进制的一些表示方法,这里使用的是java语言(注意:在java中是不允许二进制表示的,允许八进制和十六进制)。
Java整型数据类型有:byte、char、short、int、long。要把它们转换成二进制的原码形式,必须明白他们各占几个字节(一个字节占8位)。
数据类型 | 所占位数 |
byte | 8 |
boolean | 8 |
short | 16 |
int | 32 |
long | 64 |
float | 32 |
double | 64 |
char | 16 |
另外二进制中只有0和1,所以正负不是用+ -加减号来表示,而是用最高位数字来表示,0表示正,1表示负,来看两个例子:
System.out.println(Integer.toBinaryString(10)); //打印结果:1010
System.out.println(Integer.toBinaryString(-10)); //打印结果:11111111111111111111111111110110
对于正数的转换比较简单,比如上面的10,转换二进制后是00000000000000000000000000001010。
但对于负数的转换稍麻烦,转换过程如下:
00000000000000000000000000001010 //取绝对值(10),转换二进制
11111111111111111111111111110101 //取反(反码)
11111111111111111111111111110110 //反码基础上+1(补码)
(一) 接下来再看具体的位运算:
& 按位与:
举例一:
System.out.println(5&6); //打印结果:4
运算过程:
00000000000000000000000000000101 //5转换二进制
00000000000000000000000000000110 //6转换二进制
00000000000000000000000000000110 //同为1为1,否则为0的原则
4 //转换十进制,结果为4
举例二:
System.out.println(-5&6); //打印结果:2
运算过程:
11111111111111111111111111111011 //-5转二进制
00000000000000000000000000000110 //6转二进制
00000000000000000000000000000010 //同为1为1,否则为0的原则
2 //转十进制,结果为2
| 按位或:
举例一:
System.out.println(-5|-6); //打印结果:-5
运算过程:
11111111111111111111111111111011 //-5转二进制
11111111111111111111111111111010 //-6转二进制
11111111111111111111111111111011 //一个为1则为1的原则
-5 //转十进制,结果为-5
~ 按位非:
举例一:
System.out.println(~-5); //打印结果:4
运算过程:
11111111111111111111111111111011 //-5转二进制
00000000000000000000000000000100 //1为0,0为1原则
4 //转换十进制,结果为4
^ 按位异或
举例一:
System.out.println(2^3); //打印结果:1
运算过程:
00000000000000000000000000000010 //2转二进制
00000000000000000000000000000011 //3转二进制
00000000000000000000000000000001 //相同为0,不同为1
1 //转换为十进制,结果为1
<< 左位移
举例一:
System.out.println(5<<3); //打印结果40
运算过程:
00000000000000000000000000000101 //5转二进制
00000000000000000000000000101000 //左移三位,低位补0
40 //转十进制,结果为40
>> 右位移
举例一:
System.out.println(5>>2); //打印结果1
运算过程:
00000000000000000000000000000101 //5转二进制
00000000000000000000000000000001 //右移两位,高位补0(补符号位数值,正补0,负补1)
举例二:
System.out.println(-5>>2); //打印结果-2
运算过程:
11111111111111111111111111111011 //-5转二进制
11111111111111111111111111111110 //右移两位,高位补1(补符号位数值,正补0,负补1)
>>> 无符号右移
举例一:
System.out.println(-5>>>2); //打印结果是1073741822
运算过程:
11111111111111111111111111111011 //-5转二进制
00111111111111111111111111111110 //右移两位,高位补0(与符号位无关,统一补0)
写到这里,再回头看为什么会有位运算呢,仔细研究便会发现一些奥妙之处,比如:
System.out.println(2<<2); //打印结果8
System.out.println(2<<3); //打印结果16
System.out.println(2<<4); //打印结果32
有没有发现什么不一样的地方? 推算得出以下公式:Y << N 等于 Y * 2^N(2的N次方)
再比如,我们要求一个整数是奇数,还是偶数,我们可以用按位与,如 N & 1 ,如果得1则表示奇数,0为偶数,当然还有很多,有兴趣的可以深入研究
PS:鉴于可读性考虑,项目中除非必要,还是尽量不使用