3.5 移位运算

移位运算操作的目标也是二进制的“位”,因此此运算也只能处理整型数值。移位运算符将数字的位向左或向右依次移动,从而产生一个新数字。Java中的移位运算符包括<<(左移)、>>(右移)、>>>(无符号右移),本节将对上述移位运算符逐一进行介绍。

提示:移位运算与前面介绍的算术运算一样也具有类型自动提升功能。也就是说,经过移位运算的结果至少是int型。

3.5.1 “<<”左移运算

左移运算符“<<”将运算符左边的整数按位向左移动运算符右边整数指定的位数。下面通过一个例子说明其工作过程,请读者按如下步骤操作。

(1)首先,取一个整数(如8),将其按位转换为二进制表示(int型有32位)。

00000000000000000000000000001000

(2)接下来按位向左移动运算符右边所指定的位数(例如,1位),在右边填充0,得到新的二进制数:

00000000000000000000000000010000

(3)最后,将二进制数转换为十进制,本例中的结果为16。

请读者用如下代码进行验证。

    1   //代码实现
    2   public class Sample3_11
    3   {
    4        public static void main(String args[])
    5        {
    6            int a=8<<1;      //左移1位
    7            System.out.println(a);
    8        }
    9   }

编译运行后,其结果如图3-16所示。

图3-15 Sample3_10的编译运行结果

提示:因为左移n位后的结果与乘2的n次方效果相同,所以在开发中的很多情况下,用左移代替乘以2n的操作。

3.5.2 “>>”右移运算

右移运算符“>>”将运算符左边的整数按位向右移动运算符右边整数指定的位数。若最高位为“1”,移动后最高位用“1”填充,否则用“0”进行填充。

下面用一个例子来说明其工作过程,请读者按如下步骤进行操作。

(1)首先,取一个整数,将其按位转换为二进制。例如,(int)−8转换为二进制数值如下。

11111111111111111111111111111000

(2)接下来按位移动运算符右边所指定的位数,例如1位。得到新的二进制数。

11111111111111111111111111111100

(3)最后,将二进制数转换为十进制,例如,步骤(2)的结果转换为十进制−4。

请读者用如下代码进行验证。

    1   //代码实现
    2   public class Sample3_12
    3   {
    4        public static void main(String args[])
    5        {
    6            int a=-8>>1;     //右移1位
    7            System.out.println(a);
    8        }
    9   }

编译运行后结果如图3-17所示。

图3-16 Sample3_11的编译运行结果

提示:因为右移n位后的结果与除以2的n次方效果相同,所以在开发中的很多情况下,用右移代替除以2n的操作。

3.5.3 “>>>”无符号右移运算

无符号右移的规则与右移差不多,不同之处是,不管最高位是0还是1,全部用0填充。请读者按如下步骤进行无符号右移操作。

(1)首先,取一个整数,将其按位转换为二进制表示。例如,(int)−8转换为二进制表示为:

11111111111111111111111111111000

(2)接下来按位移动运算符右边整数所指定的位数,例如1位,得到新的二进制数:

01111111111111111111111111111100

(3)最后,将二进制数转换为十进制数。例如,步骤(2)的结果转换为十进制2147 483644。

请读者用如下代码进行验证。

    1   //代码实现
    2   public class Sample3_13
    3   {
    4        public static void main(String args[])
    5        {
    6            int a=-8>>>1;    //无符号右移1位
    7            System.out.println(a);
    8        }
    9   }

编译运行后,其结果如图3-18所示。

图3-17 Sample3_12的编译运行结果

提示:在进行无符号右移时要慎重,因为一个绝对值很小的负数移动后可能成为绝对值很大的正数,这在大多数情况下没有意义。

3.5.4 具体实例

在Java中,有时由于对移位运算的底层操作机制不了解,会引发一些不必要的错误,下面将通过一个例子来说明这个问题。请读者先考察下面的代码。

    1   //代码实现
    2   public class Sample3_14
    3   {
    4        public static void main(String args[])
    5        {
    6            int i=88>>32;
    7            long l=67<<64;
    8            System.out.println("i="+i);
    9            System.out.println("l="+l);
    10       }
    11  }

在看完上述代码后,读者可能认为将打印两个0。因为i是int型,有32位,l是long型,有64位,正好都移出去了,填充的又是0。其实不然,请读者看运行结果,如图3-19所示。

图3-18 Sample3_13的编译运行结果

从图3-19 中可以看出,两个数都没有变。这是因为,在进行移位前,Java系统首先把要移的位数与被移数的位数求余数,然后移动余数个位数。在本例中32对32求余数,64对64求余数,结果都为0,也就是没有移动。这个机制说明Java很聪明,因为把一个整数移动比其位数还多的位数是没有价值的。

图3-19 Sample3_14的运行结果