Java中的位运算符、移位运算符
1 概述
Java中的位运算符有:&
(与)、|
(或)、^
(异或)、~
(取反)。
移位运算符有:<<
(左移)、>>
(右移)、>>>(无符号右移),没有<<<
运算符。
2 位运算符
计算口诀
运算 | 规则 |
---|---|
& | 全1为1 |
| | 有1为1 |
^ | 相异为1 |
~ | 全部取反 |
2.1 $(与)
有0为0,全1为1
2.2 |(或)
有1为1,全0为0
2.3 ^(异或)
相同为0,相异为1
2.4 ~(非)
0变1,1变0
3 移位运算
在阅读源码的过程中,经常会看到这些符号<< ,>>,>>>,这些符号在Java中叫移位运算符,在写代码的过程中,虽然我们基本上不会去写这些符号,但需要明白这些符号的运算原理,比如HashMap中有以下代码:
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;//1左移4位为16
static final int MAXIMUM_CAPACITY = 1 << 30;//1左移30位为1073741824
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);//无符号右移
}
上段代码中就包含左移运算符<<,无符号右移运算符>>>。
接下来我们以Java中的int类型为例介绍三种移位运算符。
首先定义int类型的变量a,b
int a = 0B00101100_01010011_01101000_11010110; //十进制743663830
int b = 0B10101100_01010011_01101000_11010110; //十进制-1403819818
3.1 左移运算符:<<
左移运算符<<
的操作是舍弃高位,低位补0.
如图是int类型a在左移1位和2位后的结果。
可以看到左移1位的值是原始值的两倍,所以可以用左移代理乘2的倍数运算。但是如果左移后的值超出了类型的最大值,结果就不可预知了,可能会变成负数,也可能比原来的值小。如图,变量a在左移两位后最高位变为了1,值就变为了负值。
程序中的写法:
public class TestDemo {
public static void main(String[] args) {
int b = 0B10101100_01010011_01101000_11010110;
int a = 0B00101100_01010011_01101000_11010110;
System.out.println("a的原始十进制:\t"+a);
System.out.println("a的原始二进制:\t"+getBinaryString(a));
System.out.println("a的左移1位二进制:\t"+getBinaryString(a<<1));
System.out.println("a的左移1位十进制:\t"+getInt(getBinaryString(a<<1)));
System.out.println("a的左移2位二进制:\t"+getBinaryString(a<<2));
System.out.println("a的左移2位十进制:\t"+getInt(getBinaryString(a<<2)));
}
static String getBinaryString(int a){
String binaryString = Integer.toBinaryString(a);//转成二进制
String zeroString = "";//补缺少的0
for (int i=0;i<32 - binaryString.length();i++){
zeroString += "0";
}
binaryString = zeroString + binaryString; //补上0的二进制
String regex = "(.{8})";
binaryString = binaryString.replaceAll(regex,"$1_");
binaryString = binaryString.substring(0,binaryString.length() - 1);
return binaryString;
}
static int getInt(String a ){
a = a.replace("_","");
char first = a.charAt(0);
a = a.substring(1);
int value = Integer.parseInt(a,2);
return first == '0' ? value:-value;
}
}
a的原始十进制: 743663830
a的原始二进制: 00101100_01010011_01101000_11010110
a的左移1位二进制:01011000_10100110_11010001_10101100
a的左移1位十进制:1487327660
a的左移2位二进制:10110001_01001101_10100011_01011000
a的左移2位十进制:-827171672
如果移动的位数大于等于类型本身的位数,会对移动的位数求余后再移动。
如 a<<40,是左移40位,大于int类型的32位,实际应该左移40%32=8位。
System.out.println("a左移8位:\t"+(a<<8));
System.out.println("a左移40位:\t"+(a<<40));
a左移8位: 1399379456
a左移40位: 1399379456
可以看到左移8位和左移40位,结果是相同的。
由于double,float在二进制中的表现比较特殊,因此不能来进行移位操作,报错,编译不过,如下图:
注意:其它几种整形byte,short移位前会先转换为int类型(32位)再进行移位,这里就不写代码测试了,大家有兴趣可自行测试。
3.2 右移运算符:>>
左移运算符>>
的操作是舍弃低位,高位补符号位.
如图是int类型a在右移1位和2位后的结果。
可以看到右移1位的值是原始值的1/2,所以可以用右移代替除2运算。和左移一样,int类型移位大于等于32位时,long类型大于等于64位时,会先做求余处理再位移处理,byte,short移位前会先转换为int类型(32位)再进行移位。以上是正数的位移,我们再来看看负数的右移运算,如图,
综上所述:右移运算符>>的运算规则也很简单,丢弃右边指定位数,左边补上符号位。
3.3 无符号右移运算符:>>>
无符号右移运算符>>>和右移运算符>>是一样的,只不过右移时左边是补上符号位,而无符号右移运算符是补上0,也就是说,对于正数移位来说等同于:>>,负数通过此移位运算符能移位成正数。
无符号右移运算符>>的运算规则也很简单,丢弃右边指定位数,左边补上0。
参考:Java中的移位运算符