Java 语言中的位运算符分为位逻辑运算符和位移运算符两类,下面详细介绍每类包含的运算符。
1. 位逻辑运算符
位逻辑运算符包含 4 个:&(与)、|(或)、~(非)和 ^(异或)。除了 ~(即位取反)为单目运算符外,其余都为双目运算符。表 1 中列出了它们的基本用法。
运算符 | 含义 | 实例 | 结果 |
---|---|---|---|
& | 按位进行与运算(AND) | 4 & 5 | 4 |
| | 按位进行或运算(OR) | 4 | 5 | 5 |
^ | 按位进行异或运算(XOR) | 4 ^ 5 | 1 |
~ | 按位进行取反运算(NOT) | ~ 4 | -5 |
1.1 与运算符
位与运算符为 &
,其运算规则是:参与运算的数字,低位对齐,高位不足的补零,如果对应的二进制位同时为 1,那么结果才为 1,否则为 0。因此,任何数与 0 进行按位与运算,其结果都为 0。
应用:
判断奇偶性:任意数 a&1=0 为偶数,a&1=1为偶数
取低位:根据运算性质,任意数a&(1<<n)-1 得到a的低n位,代码举例如下:
public static void main(String[] args) {
System.err.println(Integer.toBinaryString(27)); //1011
System.err.println(Integer.toBinaryString(11));// 11011
System.err.println(11 & (1 << 3) - 1); //3 即011
System.err.println(27 & (1 << 3) - 1); //3
System.err.println(27 & (1 << 4) - 1); //11 即1101
}
利用取低位的性质,得到另一个应用:任意数(0除外)a&(a-1)==0即表示a是2的次方数
取余:a%(2^n) = a&(1<<n-1) 注意这里的^是幂函数,仅限于除数是2的次方数如a%16=a&15
1.2 或运算符
位或运算符为 |
,其运算规则是:参与运算的数字,低位对齐,高位不足的补零。如果对应的二进制位只要有一个为 1,那么结果就为 1;如果对应的二进制位都为 0,结果才为 0。
应用:后位置1(利用的就是加粗部分的运算特性),如hashmap的tableSizeFor方法
1.3 异或运算符
位异或运算符为^
,其运算规则是:参与运算的数字,低位对齐,高位不足的补零,如果对应的二进制位相同(同时为 0 或同时为 1)时,结果为 0;如果对应的二进制位不相同,结果则为 1。
特性:a ^ a = 0,a ^ 0 = a,(a ^ b) ^ c = a ^ (b ^ c)
应用:不使用临时变量,交换两个数的值
a ^= b;
b ^= a;
a ^= b;
分析:对应这三步
a = a ^ b;
b = a ^ b = (a ^ b) ^ b = a ^ (b ^ b) = a ^ 0 = a;
a = a ^ b = (a ^ b) ^ b = (a ^ a) ^ b = b ^ 0 = b
附,该题还有其他解法如:
a = a + b;
b = a - b;
a = a - b;
1.4 取反运算符
位取反运算符为~
,其运算规则是:只对一个操作数进行运算,将操作数二进制中的 1 改为 0,0 改为 1。
应用:(这里涉及到负数补码的知识)
求相反数: ~a + 1
求绝对值: a >> 31 == 0 ? a : (~a + 1)
==================================================================
2. 位移运算符
参考文章负数位运算的右移操作-C语言基础 - 十一一个人 - 博客园
首先需要了解一下原码,反码,补码的知识:原码,补码,反码_Mr_moving-CSDN博客
在计算机中,数值一律用补码来表示(存储),无论是左移还是右移,都是以此为前提进行操作。
2.1 右移:
右移分为有符号右移和无符号右移(java中用>>>符号表示),区别仅在于有符号右移,符号位不移动保持不变,无符号右移,高位统一补0,所以负数会变正数
下面是有符号右移的例子
首先是 +5>>2 = 1
+5
原码 0000 …… 0101
补码 0000 …… 0101
>>2(正数右移高位补0)
补码 0000 …… 0001
原码 0000 …… 0001
= 1-5
原码 1000 …… 0101
反码 1111 …… 1010 负数的反码是保留符号位不变源码取反
补码 1111 …… 1011 补码是反码加1
>>2 (负数右移高位补1)
补码 1111 …… 1110
反码 1111 …… 1101 补码转反码减1
源码 1000 … 0010 负数反码转源码保留符号位不变取反
= -2
无符号右移
-5
原码 1000 …… 0101
反码 1111 …… 1010 负数的反码是保留符号位不变源码取反
补码 1111 …… 1011 补码是反码加1
>>>2(无符号右移,高位补0)
补码 0011 …… 1110
反码 (此时符号位已经变为0了,系统会当成正数来处理,原反补码均一样)
原码 0011 …… 1110
= 这个不好说,得看操作系统的位数(在我这里int为32位,
结果为:107374182==>00111111 11111111 11111111 11111110)
2.2 左移:
左移相对于右移来说,就十分朴实无华,不在乎符号位,也就不存在无符号左移的说法,因为左移就是在低位补0
无论是有符号的正数还是负数,其左移都是在其补码的基础上面左移,而且低位都是补0。看到这里,你是否就意识到了一个问题,“如果正数的补码在左移的过程中,刚好有一个1移到了最高位,那么是否就会变成了负数呢?”嗯,这种情况确实是会发生的。负数左移到一定值的时候也会变成正。
2.3 注意事项
1. 在移位运算时,byte、short和char类型移位后的结果会变成int类型,所以一般是以32位的补码形式运算。
2. 当移位较大时,比如移动33位,对于byte、short、char和int进行移位时,规定实际移动的次数是移动次数和32的余数,也就是移位33次和移位1次得到的结果相同。移动long型的数值时,规定实际移动的次数是移动次数和64的余数,也就是移动66次和移动2次得到的结果相同。
2.4 位移运算的应用
在计算机系统中通常是以补码的形式存在的,以16位int数据为例,其中最为特殊的就 -1的补码 , -1的补码为:1111 1111 1111 1111;通过-1>>>n可以获取(二进制)从低位到指定位n全部为1的值;同时使用-1<< m可以获取从指定位m到符号位全部为1的值;通过(-1>>>n)&(-1<< m)就可以获取二进制中指定位置(低位到高位)全部为1的int值。
==================================================================
附运算符优先级,没必要记,括号是万能的。
优先级 | 运算符 |
---|---|
1 | ()、[]、{} |
2 | !、+、-、~、++、-- |
3 | *、/、% |
4 | +、- |
5 | «、»、>>> |
6 | <、<=、>、>=、instanceof |
7 | ==、!= |
8 | & |
9 | ^ |
10 | | |
11 | && |
12 | || |
13 | ?: |
14 | =、+=、-=、*=、/=、&=、|=、^=、~=、«=、»=、>>>= |