Java中的位运算符

Java中的位运算符

文本关键字:位运算符、位逻辑运算符、移位运算符

一、位运算符

大家在接触运算符的时候通常都已经学完了变量的使用,对于算术以及赋值运算的感觉就是So easy!这不就是小学的知识嘛,对于逻辑运算符的部分依然无压力,这不就是中学的知识嘛?但是突然出现了一个位运算符,啥是移位?啥是异或?接下来就先从简单的开始。
说起位运算符,其实就是基于数据存储的二进制位进行的运算,更底层,所以效率更高。另外一个需要注意的问题就是:由于小数在进行存储的时候采用的是IEEE(符号、指数、尾数)方式,并不止对整数和小数部分直接转换为二进制来存储的,所以小数是不能使用位运算符来操作的。对于整数和字符型的运算符操作也有一些潜在的法则,相信看完这篇文章你很容易就会掌握。

二、逻辑运算

在逻辑运算中我们已经使用过能够表达逻辑意义的运算符,如:&&,||,!。这些运算符都有一个共同点,那就是:运算符两边都是布尔值或布尔表达式,他们能够操作的数据类型有限,只能够帮我们进行逻辑运算。有些教材将&,|等位运算符也归为逻辑运算符,因为按位与(&)、按位或(|)能够操作的数据类型较多,其中就包括布尔类型,并且也能够帮助我们进行逻辑运算,但是小编还是建议按照符号本身的运算方式和操作数据类型等来记忆。

1. 与(&)

  • 与运算

与运算相当于物理电路中的串联电路,我们假设用1代表通路,用0代表断路,那么对于串联电路来说,只有当运算符两边全为1(通路)时,运算结果才为1(通路)。
Java中的位运算符
Java中的位运算符

  • 按位与

那么按位与就是将运算符两边的数字转换为二进制后,在每两个对应位置上的数字进行与运算,再将最后的结果按十进制写出就可以了。
24 & -30:
00000000 00000000 00000000 00011010 = 26
11111111 11111111 11111111 11100010 = -30
00000000 00000000 00000000 00000010 = 26 & -30 = 2

  • 逻辑与的短路问题

当我们在使用逻辑与(&&)时会遇到一个短路问题:当用&&把多个布尔表达式连接起来的时候,为了以最快的速度得出结果,那么有些式子将不会执行,被跳过的式子中的代码也就不会被执行。比如:(假式 && 真式 && 真式),经过前两个式子的结果已经能够确定整个式子的结果:为假,第三个式子无论为真或假都不会影响最终结果,这个时候就会进行跳过。
但是对于&(按位与),由于本质上是一个位运算,只不过同时也支持布尔类型的直接运算而已,所以不会出现表达式不执行的情况。

2. 或(|)

  • 或运算

或运算相当于物理电路中的并联电路,我们假设用1代表通路,用0代表断路,那么对于并联电路来说,只要运算符两边有一个为1(通路)时,运算结果就为1(通路)。
Java中的位运算符
Java中的位运算符

  • 按位或

那么按位或就是将运算符两边的数字转换为二进制后,在每两个对应位置上的数字进行或运算,再将最后的结果按十进制写出就可以了。
26 | -30:
00000000 00000000 00000000 00011010 = 26
11111111 11111111 11111111 11100010 = -30
11111111 11111111 11111111 11111010 = 26 | -30 = -6

  • 逻辑或的短路问题

当我们在使用逻辑或(||)时会遇到一个短路问题:当用||把多个布尔表达式连接起来的时候,为了以最快的速度得出结果,那么有些式子将不会执行,被跳过的式子中的代码也就不会被执行。比如:(真式 || 假式 && 假式),经过前两个式子的结果已经能够确定整个式子的结果:为真,第三个式子无论为真或假都不会影响最终结果,这个时候就会进行跳过。
但是对于|(按位与),与按位或相同,是一个位运算符,不会出现跳过的情况。

3. 取反(~)

  • 运算规则

取反运算的规则相对简单,同样是在二进制位上的运算,那么遇到0变为1,遇到1变为0。
Java中的位运算符

  • 实用公式

对于一个数n的取反可以按照公式快读得出结果:~n = -n - 1

4. 异或(^)

  • 运算规则:

其实异或的运算规则很好记,对对碰原则,两个数相同为0,不同为1,如下表:
Java中的位运算符

  • 运算律:
    • a ^ a = 0
    • 交换律:a ^ b = b ^ a
    • 结合律:a ^ (b ^ c) = (a ^ b) ^ c
    • a ^ b ^ a = b
  • 两数交换的用法:

如果我们需要将两个数交换,一般都需要引入第三个变量,作为中间变量或进行运算实现。其实,我们还可以利用异或运算的规律来实现:不借助第三个变量来实现两个数的交换。
a = a ^ b;
b = a ^ b; -> 使用交换律:b = (a ^ b) ^ b = (b ^ a) ^ b = a
a = a ^ b; -> 此时b的值为a,a的值为:(a ^ b) -> a = (a ^ b) ^ a = b
特别注意:虽然异或可以用于两个数的交换,但是由于异或有一个特别的性质,即a ^ a = 0,所以当两个数相等时,会导致两个数都变为0

三、移位运算

特别注意:对于位移运算不能直接和除法的结果等同,在负数时将不相等

1. 左移(<<)

将一个整数的二进制位向左平移指定的位数,由于是左移就相当于是扩大了数值,并且每移动一位,相当于扩大了二倍。

  • 移出的高位将被丢弃
  • 右侧补零

由于正数的高位都是0,负数的高位都是1,所以在一定的范围内,经过向左移位不会改变数字的正负。但如果超出了一定的范围,将会改变数字的正负,并且会充满随机性,正负性将取决于最高位(符号位)的数值。
-7 << 2 = -28
-7 << 30 = 1073741824

2. 右移(>>)

将一个整数的二进制位向右平移指定的位数,由于是右移就相当于是缩小了数值,并且每移动一位,相当于缩小了二倍。

  • 移出的低位将被丢弃
  • 若为正数,高位补0
  • 若为负数,高位补1

由于符号位在高位的部分,并且在移动的过程中的补位也是根据正负的规则在补,所以右移不会改变正负。

3. 无符号右移(>>>)

无符号右移的计算规则与右移相同,区别在于,不会进行正负的区分,高位一律用0补位。如果原数是一个负数,则可能直接得到一个非常大的正数。

  • -27 >> 2 = -7
  • -27 >>> 2 = 1073741817

    四、运算规则

    1. 运算符两边精度不一致

  • 需要先进行精度的统一,即以高精度为准,低精度的数据进行补位
    • 正数高位补0
    • 负数高位补1

      2. 移位运算规则

  • 移动的位数不应该超过该数字对应的二进制位数
    • 得到的结果无数学意义
    • 会得到一些极端值结果
上一篇:07-10-位运算


下一篇:Java基本数据类型及其运算