目录

  • 1 概述
  • 2 位运算符
  • 2.1 $(与)
  • 2.2 |(或)
  • 2.3 ^(异或)
  • 2.4 ~(非)
  • 3 移位运算
  • 3.1 左移运算符:<<
  • 3.2 右移运算符:>>
  • 3.3 无符号右移运算符:>>>

1 概述

Java中的位运算符有:&(与)、|(或)、^(异或)、~(取反)。
移位运算符有:<<(左移)、>>(右移)、>>>(无符号右移),没有<<<运算符。

2 位运算符

计算口诀

运算

规则

&

全1为1

|

有1为1

^

相异为1

~

全部取反

2.1 $(与)

有0为0,全1为1

java左移运算符 java 移位运算符_十进制

2.2 |(或)

有1为1,全0为0

java左移运算符 java 移位运算符_运算符_02

2.3 ^(异或)

相同为0,相异为1

java左移运算符 java 移位运算符_运算符_03

2.4 ~(非)

0变1,1变0

java左移运算符 java 移位运算符_System_04

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

java左移运算符 java 移位运算符_运算符_05

3.1 左移运算符:<<

左移运算符<<的操作是舍弃高位,低位补0.

如图是int类型a在左移1位和2位后的结果。

java左移运算符 java 移位运算符_System_06


可以看到左移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在二进制中的表现比较特殊,因此不能来进行移位操作,报错,编译不过,如下图:

java左移运算符 java 移位运算符_运算符_07

注意:其它几种整形byte,short移位前会先转换为int类型(32位)再进行移位,这里就不写代码测试了,大家有兴趣可自行测试。

3.2 右移运算符:>>

左移运算符>>的操作是舍弃低位,高位补符号位.

如图是int类型a在右移1位和2位后的结果。

java左移运算符 java 移位运算符_十进制_08

可以看到右移1位的值是原始值的1/2,所以可以用右移代替除2运算。和左移一样,int类型移位大于等于32位时,long类型大于等于64位时,会先做求余处理再位移处理,byte,short移位前会先转换为int类型(32位)再进行移位。以上是正数的位移,我们再来看看负数的右移运算,如图,

java左移运算符 java 移位运算符_System_09


java左移运算符 java 移位运算符_十进制_10

综上所述:右移运算符>>的运算规则也很简单,丢弃右边指定位数,左边补上符号位。

3.3 无符号右移运算符:>>>

无符号右移运算符>>>和右移运算符>>是一样的,只不过右移时左边是补上符号位,而无符号右移运算符是补上0,也就是说,对于正数移位来说等同于:>>,负数通过此移位运算符能移位成正数。

java左移运算符 java 移位运算符_运算符_11


java左移运算符 java 移位运算符_System_12

无符号右移运算符>>的运算规则也很简单,丢弃右边指定位数,左边补上0。