
按 位运算符 允许我们操作一个整数主数据类型中的单个“比特”,即二进制位。按位运算符会对两个自变量中对应的位执行 布尔代数 ,并最终生成一个结果。
按 位运算 来源于 C 语言的低级操作。我们经常都要直接操纵硬件,需要频繁设置硬件寄存器内的二进制位。
Java 的设计初衷是嵌入电视顶置盒内,所以这种低级操作仍被保留下来了。然而,由于操作系统的进步,现在也许不必过于频繁地进行按位运算。
若两个输入位都是 1 ,则按位 AND 运算符( & )在输出位里生成一个 1 ;否则生成 0 。若两个输入位里至少有一个是 1 ,则按位 OR 运算符( | )在输出位里生成一个 1 ;只有在两个输入位都是 0 的情况下,它才会生成一个 0 。若两个输入位的某一个是 1 ,但不全都是 1 ,那么按位 XOR ( ^ ,异或)在输出位里生成一个 1 。按位 NOT ( ~ ,也叫作“非”运算符)属于一元运算符;它只对一个自变量进行操作(其他所有运算符都是二元运算符)。按位 NOT 生成与输入位的相反的值——若输入 0 ,则输出 1 ;输入 1 ,则输出 0 。
按位运算符和逻辑运算符都使用了同样的字符,只是数量不同。因此,我们能方便地记忆各自的含义:由于“位”是非常“小”的,所以按位运算符仅使用了一个字符。
按位运算符可与等号( = )联合使用,以便合并运算及赋值: &= , |= 和 ^= 都是合法的(由于 ~ 是一元运算符,所以不可与 = 联合使用)。
我们将 boolean ( 布尔 )类型当作一种“单位”或“单比特”值对待,所以它多少有些独特的地方。我们可执行按位 AND , OR 和 XOR ,但不能执行按位 NOT (大概是为了避免与逻辑 NOT 混淆)。对于 布尔值 ,按位运算符具有与逻辑运算符相同的效果,只是它们不会中途“短路”。此外,针对布尔值进行的按位运算为我们新增了一个 XOR 逻辑运算符,它并未包括在“逻辑”运算符的列表中。在移位表达式中,我们被禁止使用 布尔运算 ,原因将在下面解释。
移位运算符
移位运算符 面向的运算对象也是二进制的“位”。可单独用它们处理整数类型(主类型的一种)。左移位运算符( << )能将运算符左边的运算对象向左移动运算符右侧指定的位数(在低位补 0 )。“有符号”右移位运算符( >> )则将运算符左边的运算对象向右移动运算符右侧指定的位数。“有符号”右移位运算符使用了 “符号扩展”:若值为正,则在高位插入 0 ;若值为负,则在高位插入 1 。 Java 也添加了一种“无符号”右移位运算符( >>> ),它使用了“零扩展”:无论正负,都在高位插入 0 。这一运算符是 C 或 C++ 没有的。
若对 char , byte 或者 short 进行移位处理,那么在移位进行之前,它们会自动转换成一个 int 。只有右侧的 5 个低位才会用到。这样可防止我们在一个int数里移动不切实际的位数。若对一个 long 值进行处理,最后得到的结果也是 long 。此时只会用到右侧的 6 个低位,防止移动超过 long 值里现成的位数。但在进行“无符号”右移位时,也可能遇到一个问题。若对 byte 或 short 值进行右移位运算,得到的可能不是正确的结果(Java 1.0和Java 1.1特别突出)。它们会自动转换成 int 类型,并进行右移位。但“零扩展”不会发生,所以在那些情况下会得到 -1 的结果。可用下面这个例子检测自己的实现方案:
//: URShift.java // Test of unsigned right shift public class URShift { public static void main(String[] args) { int i = -1; i >>>= 10; System.out.println(i); long l = -1; l >>>= 10; System.out.println(l); short s = -1; s >>>= 10; System.out.println(s); byte b = -1; b >>>= 10; System.out.println(b); } } ///:~
移位可与等号( <<= 或 >>= 或 >>>= )组合使用。此时,运算符左边的值会移动由右边的值指定的位数,再将得到的结果赋回左边的值。