最近又回去重新看了java基础的书籍,在记录总结以前一些比较容易混淆的知识点。下面是本篇要记录的内容

java 位运算工具包_java位运算详解


一、 相关基础概念

在开始java位运算的知识之前,我们先来了解几个基础的概念,机器数,真值,原码,反码,补码。

1.机器数

我们知道无论是代码还是数值,在计算机中最后都转换成以二进制的形式存在的,而一个数值在计算机中的二进制表示形式,就是这个数的机器数。机器数是有符号位的,在计算机中用一个二进制数的最高位存放符号,正数为0,负数为1,如下实例(按原码表示):

十进制的+5,计算机字长为8位,其二进制就是00000101

十进制的 -5,计算机字长为8位,其二进制就是10000101(这里用的是原码)

其中00000101和10000101就是机器数

2.真值

由于机器数的第一位是符号位,所以其形式值就不等于其真值的数值,也就是说10000101表示的是-5而不是133(10000101的十进制是131,前提是不算最高位为符号位),因此-5才是机器数的真值。

3.原码

    原码是一种计算机中对数字的二进制定点表示方法。原码表示法在数值前面增加了一位符号位(即最高位为符号位):正数该位为0,负数该位为1,其余位表示数值的大小。

[+5]=[00000101](原码)

[ -  5]=[10000101](原码)

因为第一位是符号位,因此8位二进制的取值范围就是[1111 1111,0111 1111]也就是[-127,127]

4.反码

反码是数值存储的一种,但是由于补码更能有效表现数字在计算机中的形式,所以多数计算机一般都不采用反码表示数,反码的表示方法如下:

  • 正数的反码是其本身
  • 负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.

[+5]=[00000101](原码)= [00000101](反码)

[ -  5]=[10000101](原码)= [11111010](反码)

5.补码

在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理。补码的表示方法是:

  • 正数的补码就是其本身
  • 负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)

[+5]=[00000101](原码)= [00000101](反码)=[00000101](补码)

[ -  5]=[10000101](原码)= [11111010](反码)=[11111011](补码)

6.小结

计算机中的符号数有三种表示方法,即原码、反码和补码。三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位,三种表示方法各不相同。而在计算机系统中,数值一律用补码来表示和存储。

二、 Java位运算

位移操作:(只针对 int类型的数据有效,java中,一个int的长度始终是32位,也就是4个字节,它操作的都是该整数的二进制数).也可作用于以下类型,即 byte,short,char,long(它们都是整数形式)。当为这四种类型时,JVM先把它们转换成int型再进行操作。

7.左移(<<)

m<<n的含义:把整数m表示的二进制数左移n位,高位移出n位都舍弃,低位补0.  (此时将会出现正数变成负数的可能),如下实例:

5<<2 :把十进制的数值5左移两位,按如下步骤计算,

  • 把5转位16位的二进制机器数:00000000 00000000 00000000 00000101
  • 按左移原理,将二进制数左移两位:00000000 00000000 00000000 00010100
  • 左移后结果为20

5<<29:把十进制的数值5左移29位,按如下步骤计算,

  • 把5转位16位的二进制机器数:00000000 00000000 00000000 00000101
  • 按左移原理,将二进制数左移29位:10100000 00000000 00000000  00000000
  • 左移后高位是1,结果显然是负数

小结:m<<n即在数字没有溢出的前提下,对于正数和负数,左移n位都相当于m乘以2的n次方.

8.右移(>>)

 m>>n的含义:把整数m表示的二进制数右移n位,m为正数,高位全部补0;m为负数,高位全部补1,实例如下:

5>>2 :把十进制的数值5右移两位,按如下步骤计算,

  • 把5转位16位的二进制机器数:00000000 00000000 00000000 00000101
  • 按右移原理,将二进制数左移两位:00000000 00000000 00000000 00000001
  • 右移后结果为1

-5>>2:把十进制的数值-5右移两位,按如下步骤计算,

  • 把-5转位16位的二进制机器数:11111111 11111111 11111111 11111011
  • 按右移原理,将二进制数右移两位:11111111 11111111 11111111 11111110
  • 右移后结果为-2

小结:

m>>n即相当于m除以2的n次方,得到的为整数时,即为结果。如果结果为小数,此时会出现两种情况:

  • 如果m为正数,得到的商会无条件 的舍弃小数位;
  • 如果m为负数,舍弃小数部分,然后把整数部分加+1得到位移后的值。

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

m>>>n:整数m表示的二进制右移n位,不论正负数,高位都补0,实例如下:

5>>>2 :把十进制的数值5右移两位,按如下步骤计算,

  • 把5转位16位的二进制机器数:00000000 00000000 00000000 00000101
  • 按右移原理,将二进制数左移两位:00000000 00000000 00000000 00000001
  • 右移后结果为1

-5>>>2:把十进制的数值-5右移两位,按如下步骤计算,

  • 把-5转位16位的二进制机器数:11111111 11111111 11111111 11111011
  • 按右移原理,将二进制数右移两位:00111111 11111111 11111111 11111110
  • 右移后结果为正数

10.按位非操作(~)

~ 按位取反操作符,对每个二进制位的内容求反,即1变成0,0变成1实例如下

  • 把-5转位16位的二进制机器数:11111111 11111111 11111111 11111011
  • ~(-5) 取反结果:00000000 00000000 00000000 00000100 
  • 转为十进制,结果为4

11.按位与操作(&)

& 位与操作符,对应的二进制位进行与操作,两个都为1才为1,其他情况均为0,原理如下:

  • 1&0=0
  • 0&0=0
  • 1&1=1
  • 0&1=0

实例:-5 & 4

-5的二进制形式为: 11111111 11111111 11111111 11111011

 4的二进制形式为:  00000000 00000000 00000000 00000100

——————————————————————————————

逻辑与运算结果:     00000000 00000000 00000000 00000000

最终结果为0。

12.按位或操作(|)

| 位或操作符,对应的二进制位进行或操作,两个都为0才为0,其他情况均为1,原理如下:

  • 1|0=1
  • 0|0=0
  • 1|1=1
  • 0|1=1

实例:-5 | 4

 -5的二进制形式为:11111111 11111111 11111111 11111011

  4的二进制形式为:00000000 00000000 00000000 00000100

————————————————————————————

逻辑或运算结果:    11111111 11111111 11111111 11111111

最终结果为-1。

利用或的原理我们可以把字节转换为整数,-64&0xFF=192,其中0xFF表示整数255。

13.按位异或操作( ^ )

^ 异或操作符,相同位值为0 否则为1,原理如下:

  • 1^1=0
  • 1^0=1
  • 0^1=1
  • 0^0=0

实例:-5 ^  4

 -5的二进制形式为:11111111 11111111 11111111 11111011

  4的二进制形式为:00000000 00000000 00000000 00000100

————————————————————————————

逻辑异或运算结果:    11111111 11111111 11111111 11111111

最终结果为-1。

其实利用逻辑异或操作有个作用就是可以比较两个数值是否相等,即利用1^1=0,0^0=0的原理,如5^5==0。

14.总结

通过上面的分析,我们对java的位运算也算有了比较全面的了解,那么我们的程序通过位运算又有什么优势呢?其实通过位运算确实会比我们直接的程序代码运算会快很多,因为位运算直接运算的是计算机底层的二进制机器操作指令,而我们的程序代码运算最终也是要转成计算机可识别的二进制操作指令才能执行,位运算可以理解为省了中间转换的操作,处理器可以直接操作。事实是我们在某些源码经常能看见如下代码:

<span style="font-family:Microsoft YaHei;">     public static final int OP_CONNECT = 1 << 3;
    /**
     * Operation-set bit for socket-accept operations.  </p>
     *
     * <p> Suppose that a selection key's interest set contains
     * <tt>OP_ACCEPT</tt> at the start of a <a
     * href="Selector.html#selop">selection operation</a>.  If the selector
     * detects that the corresponding server-socket channel is ready to accept
     * another connection, or has an error pending, then it will add
     * <tt>OP_ACCEPT</tt> to the key's ready set and add the key to its
     * selected-key set.  </p>
     */
    public static final int OP_ACCEPT = 1 << 4;</span>


其实原理是一样的,处理器能够直接支持和处理。