CodeGym /Java 博客 /China /Java 位运算符
作者
Alex Vypirailenko
Java Developer at Toshiba Global Commerce Solutions

Java 位运算符

已在 China 群组中发布
在今天的课程中,我们将熟悉 Java 的位运算符,并考虑一些如何使用的例子。你可能对“比特”这个词很熟悉。如果不熟悉,我们来回忆一下是什么意思:) 一个位是计算机中最小的信息单位。它的名字来自二进制数字。一个位可以用两个数字中的一个来表示:1 或 0。有一种基于 1 和 0 的特殊二进制数字系统。 我们不会在这里钻研数学。我们只注意到 Java 中的任何数字都可以转换成二进制形式。为此,你需要使用包装类。 例如,你可以这样对一个 int 变量使用包装类:

public class Main {

   public static void main(String[] args) {

       int x = 342;
       System.out.println(Integer.toBinaryString(x));
   }
}
控制台输出:
101010110
1010 10110(我加了空格是为了方便阅读)是十进制中的数字 342。我们实际上已经把这个数字分解成了单个的比特:0 和 1。对位执行的操作称为按位操作
  • ~ — 按位非。
这个运算符非常简单:它传递数字的每一个位,并翻转该位:0 变成 1,1 变成 0。 如果将按位非应用到数字 342,则发生如下事项:
101010110 是用二进制数表示的 342 010101001 是表达式 ~342 的值
让我们试着将其付诸实施:

public class Main {

   public static void main(String[] args) {

       int x = 342;
       System.out.println(~x);
   }
}
控制台输出:
169
169 是我们熟悉的十进制系统中的结果 (010101001) :)
  • & — 按位与
如你所见,它看起来非常类似于逻辑与 (&&)。 你会记得,只有当两个操作数都为真时,&& 运算符才返回真。按位 & 的工作方式类似:它逐位比较两个数字。比较产生第三个数字。 例如,让我们以数字 277 和 432 为例:
110110000 是用二进制数表示的 277 1000101011 是用二进制数表示的 432
接下来,运算符 & 将高位数字的第一位与低位数字的第一位进行比较。因为这是一个与运算符,所以只有两位都为 1 时,结果才会为 1。在任何其他情况下,结果为 0。 100010101 & 110110000 _______________ 10001000 — & 运算符的结果 首先,我们比较两个数组的第一位、第二位、第三位,依此类推。 正如你所看到的,只有在两种情况下,数字中对应的两个位都等于 1(第一位和第五位)。所有其他比较都产生 0。 所以最后我们得到了数字 10001000。在十进制系统中,它对应于数字 272。我们检查下:

public class Main {

   public static void main(String[] args) {
       System.out.println(277&432);
   }
}
控制台输出:
272
  • | — 按位或。
该运算符的工作方式是一样的:逐位比较两个数字。只是现在如果至少有一位是 1,那么结果就是 1。让我们看看同样的数字(277 和 432): 100010101 | 110110000 _______________ 110110101 — | 运算符的结果 这里我们得到一个不同的结果:唯一保持为 0 的位是那些在两个数中都为 0 的位。 结果是数字 110110101。在十进制系统中,它对应于数字 437 我们检查下:

public class Main {

   public static void main(String[] args) {
       System.out.println(277|432);
   }
}
控制台输出:
437
我们计算的一切都是正确的!:)
  • ^ — 按位异或
我们还没有遇到该操作符。但这没什么复杂的。它类似于普通的或运算符。区别是,如果至少有一个操作数为真,普通或返回真。但不一定是一个操作数:如果两个操作数都为真,结果也为真。 但只有当其中一个操作数为真时,异或运算才返回真。 如果两个操作数都为真,普通或返回真(“至少一个为真”),但异或返回假。这就是为什么它被称为异或。 知道了前面的按位运算符是如何工作的,你可能很容易计算出 277 ^ 432。 但是让我们一起再深入研究一次 :) 100010101 ^ 110110000 _______________ 010100101 — ^ 运算符的结果 这就是我们的结果。两个数中相同的那些位产生0(意味着“只有一个”测试失败)。但是组成 0-1 或 1-0 对的位变成了 1。 我们所得的结果是数字 010100101。在十进制中,它对应于数字 165。 让我们看看计算是否正确:

public class Main {

   public static void main(String[] args) {
       System.out.println(277^432);
   }
}
控制台输出:
165
超赞!一切正如我们所料 :) 现在该熟悉位移运算符了。 该运算符名字本身就不言自明。我们取一个数,将其位向左或向右移动 :)让我们看看变成什么样子:

左移

比特向左移位由 << 指示 下面是一个示例:

public class Main {

   public static void main(String[] args) {
       int x = 64;//value
       int y = 3;// Shift distance

       int z = (x << y);
       System.out.println(Integer.toBinaryString(x));
       System.out.println(Integer.toBinaryString(z));
   }
}
在这个例子中,数字 x = 64 称为值。我们要移动的是值的一部分。我们将把这些位向左移(你可以通过 << 操作符的方向猜到这一点) 在二进制系统中,数字 64 = 1000000 数字 y = 3 称为移动距离。移位距离表示你想要将数字 x 的位向右/向左移位多少位 在例子中,我们将将数字左移 3 位。 为了更清楚地看到移位过程,请参见图片。 在此例子中,我们使用 int。int 在计算机内存中占 32 位。这是最初的数字 64 的样子: Java 位运算符 - 1现在我们把每一位都向左移动 3 个位置: Java 位运算符 - 2看看我们得到了什么。如你所见,我们所有的位都已移位,并且从范围的边缘添加了另外 3 个零。三,因为我们移动了 3 位。如果我们移位 10,就会增加 10 个零。

因此,表达式 x << y 表示“将数字 x 的位向左移动 y 位”。表达式的结果是数字 1000000000,在十进制中是 512。 我们检查下:


public class Main {

   public static void main(String[] args) {
       int x = 64;//value
       int y = 3;// Shift distance

       int z = (x << y);
       System.out.println(z);
   }
}
控制台输出:
512
完全正确! 理论上,这些位可以无限移位,但是因为我们的数字是 int,所以我们只有 32 个二进制数字可用。其中,7 个已经被 64 (1000000) 占用。 因此,如果我们向左移动 27 个位置,我们唯一的一个位置将超出数据类型的范围而丢失。 只有零会留下!

public class Main {

   public static void main(String[] args) {
       int x = 64;//value
       int y = 26;// Shift distance

       int z = (x << y);
       System.out.println(z);
   }
}
控制台输出:
0
不出所料,1 移到 32 个可用位之外并消失了。我们以一个仅由零组成的 32 位数字结束。 Java 位运算符 - 3自然,这对应的是十进制中的 0。 记住向左移动有一个简单的规则: 每向左移动一次,数字就乘以 2。 让我们试着在没有比特图的情况下计算下面的表达式 111111111 << 3 我们需要将数字 111111111 乘以 2。结果我们得到 888888888。让我们编写一些代码并进行检查:

public class Main {

   public static void main(String[] args) {
       System.out.println(111111111 << 3);
   }
}
888888888

右移

该操作由 >> 表示。 该操作功能相同,但方向不同!:) 我们不会重新发明轮子。我们使用同一个 int 64 试试。

public class Main {

   public static void main(String[] args) {
       int x = 64;//value
       int y = 2;// Shift distance

       int z = (x >> y);
       System.out.println(z);
   }
}
Java 位运算符 - 4Java 位运算符 - 5由于向右移动了 2 位,我们数字中的两个极远端的零移出了范围并丢失了。我们得到 10000,这相当于十进制中的数字 16 控制台输出:
16
记住向右移动有一个简单的规则: 每次向右移位除以 2,丢弃任何余数。 例如: 35 >> 2 意味着我们需要将 35 除以 2 两次,去掉余数
35/2 = 17(去掉余数 1) 17/2 = 8(去掉余数 1)
最终,35 >> 2 应该等于 8。 我们检查下:

public class Main {

   public static void main(String[] args) {
       System.out.println(35 >> 2);
   }
}
控制台输出:
8

Java 中的运算符优先级

在编写和阅读代码时,你经常会发现组合了几种运算的表达式。了解这些表达式的执行顺序非常重要(否则,你可能会对结果感到惊讶) 因为 Java 有很多操作,每个操作都在一个特殊的表中被分配了一个位置:

运算符优先级

运算符 优先级
后缀 expr++ expr--
一元运算符 ++expr --expr +expr ~ !
乘法 * / %
相加 + -
移位 << >> >>>
关系 < > <= >= instanceof
相等 == !=
按位与 &
按位异或 ^
按位或 |
逻辑与 &&
逻辑或 ||
三元运算符 ? :
赋值 = += -= *= /= %= &= ^= |= <<= >>= >>>=
所有操作都从左到右执行,并考虑其优先级。 例如,如果我们编写此程序:

int x  = 6 - 4/2;
那么将首先执行除法运算 (4/2)。尽管位置在第二位,但其优先级更高。 圆括号和方括号表示最大优先级。你可能还记得在学校的时候学习过。 例如,如果将它们添加到表达式中

int x  = (6 - 4)/2;
然后首先执行减法,因为它是用括号括起来的。

boolean x = 6 - 4/2 > 3 && 12*12 <= 119;

boolean x = 6 - 2 > 3 && 12*12 <= 119;
  • 12*12 = 144

boolean x = 6 - 2 > 3 && 144 <= 119;
  • 6-2 = 4

boolean x = 4 > 3 && 144 <= 119;
接下来,执行比较运算符:
  • 4 > 3 = true

boolean x = true && 144 <= 119;
  • 144 <= 119 = false
Java 位运算符 - 6

boolean x = true && false;
最后,与运算符 (&&) 将在最后执行。

boolean x = true && false;
boolean x = false;
例如,加法 (+) 运算符的优先级高于 !=(不等)比较运算符; 因此,在表达式中

boolean x = 7 != 6+1;
将首先执行 6+1 运算,然后执行 7 != 7 检查(计算结果为假),最后将结果 (false) 赋值给变量 x(赋值运算一般在所有运算符中优先级最低;见表)。 唷!这是一个很大的教训,但你做到了!如果你没有完全理解本课程或之前的课程,请不要担心。将来我们会不止一次地谈到这些话题。 一些关于逻辑和数字运算的 CodeGym 课程。我们不会很快谈到这些内容,但你现在读一读也无妨。
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION