3

首先我了解到&, |,^是按位运算符,现在有人用 , 将它们称为逻辑运算符&&||我完全糊涂了——同一个运算符有两个名字?已经有逻辑运算符&&, ||, 那为什么还要使用&, |,^呢?

4

4 回答 4

20

Java 运算符&|^按位运算符或逻辑运算符...取决于操作数的类型。如果操作数是整数,则运算符是按位的。如果它们是布尔值,则运算符是逻辑的。

这不仅仅是我这么说的。JLS 也以这种方式描述这些运算符;见JLS 15.22

(这就像+意味着加法或字符串连接......取决于操作数的类型。或者就像“玫瑰”意味着花或淋浴附件。或者“猫”意味着毛茸茸的动物或 UNIX命令。单词在不同的上下文中意味着不同的东西。编程语言中使用的符号也是如此。)


已经有逻辑运算符&&, ||, 为什么要使用&, |, ^?

在前两个的情况下,这是因为运算符在何时/是否对操作数进行评估方面具有不同的语义。在不同的情况下需要两种不同的语义;例如

    boolean res = str != null && str.isEmpty();

相对

    boolean res = foo() & bar();  // ... if I >>need<< to call both methods.

^运营商没有短路等效物,因为拥有一个根本没有意义。

于 2012-07-22T05:43:21.767 回答
3

拥有语言参考是一回事,正确解释它是另一回事。

我们需要正确地解释事物。

即使 Java 记录&了按位和逻辑,我们也可以提出一个论点,&自远古以来,它确实没有失去逻辑运算符的魔力,因为 C。也就是说,&首先是一个固有的逻辑运算符(尽管一个非短路的那个)

&将词法+逻辑解析为逻辑操作。

为了证明这一点,这两行的行为都是一样的,从 C 到现在(Java、C#、PHP 等)

if (a == 1 && b)

if (a == 1 & b)

也就是说,编译器会将这些解释为:

if ( (a == 1) && (b) )

if ( (a == 1) & (b) )

即使两个变量ab都是整数。这个...

if (a == 1 & b)

...仍将被解释为:

if ( (a == 1) & (b) )

因此,这将在不利于整数/布尔对偶性的语言上产生编译错误,例如 Java 和 C#:

if (a == 1 & b)

事实上,在上面的编译错误上,我们甚至可以提出一个&没有失去逻辑(非短路)操作的论点 mojo,我们可以得出结论,Java 延续了 C 的传统,使&仍然是一个逻辑操作。因此,我们可以说它是相反的,即&可以重新用作按位运算(通过应用括号):

if ( a == (1 & b) )

所以我们在另一个平行宇宙中,有人可能会问,如何使&表达式成为位掩码操作。

如何进行以下编译,我在 JLS 中读到这&是按位运算。a 和 b 都是整数,但它让我无法理解为什么以下按位运算是 Java 中的编译错误:

如果 (a == 1 & b)

或者这样的问题:

为什么以下没有编译,我在 JLS 中读到,&当它的两个操作数都是整数时,它是按位运算。a 和 b 都是整数,但它让我无法理解为什么以下按位运算是 Java 中的编译错误:

如果 (a == 1 & b)

事实上,如果已经存在类似于上述问题的现有 stackoverflow 问题,我不会感到惊讶,这些问题询问如何在 Java 中执行该掩码习语。

为了使该语言的逻辑操作解释变为按位,我们必须这样做(在所有语言上,C、Java、C#、PHP 等):

if ( a == (1 & b) )

所以要回答这个问题,这并不是因为 JLS 以这种方式定义事物,而是因为 Java(以及受 C 启发的其他语言)的&运算符是出于所有意图和目的仍然是一个逻辑运算符,它保留了 C 的语法和语义。C 开始,从远古时代开始,甚至在我出生之前就是这样。

事情不是偶然发生的,JLS 15.22 不是偶然发生的,它有着悠久的历史。

在另一个&&没有引入语言的平行宇宙中,我们仍将&用于逻辑运算,今天人们甚至可能会问一个问题:

是吗,我们可以使用逻辑运算符&进行按位运算吗?

& 不关心它的操作数是否为整数、布尔值与否。它仍然是一个逻辑运算符,一个非短路运算符。事实上,在 Java 中(甚至在 C 中)强制它成为位运算符的唯一方法是在它周围加上括号。IE

if ( a == (1 & b) )

想想看,如果&&没有引入 C 语言(以及任何复制其语法和语义的语言),现在任何人都可能会问:

如何&用于按位运算?

总而言之,首先Java&本质上是一个逻辑运算符(非短路运算符),它不关心其操作数,即使两个操作数都是整数(例如掩码习语)。您只能通过应用括号来强制它成为按位运算。Java 延续了 C 的传统

如果 Java 的操作数(下面的示例代码中的整数 1 和整数变量)都是整数,&那么它确实是按位运算,则应该编译:b

 int b = 7;
 int a = 1;

 if (a == 1 & b) ...
于 2012-07-23T11:35:08.717 回答
1

它们(&|)很久以前被用于两个目的,逻辑运算符和位运算符。如果您要查看新生的 C 语言(Java 的模式化语言),&并被|用作逻辑运算符。

但是由于在同一个语句中区分位运算和逻辑运算是非常令人困惑的,它促使丹尼斯·里奇为逻辑运算符创建一个单独的运算符(&&||)。

在此处查看新生儿 C部分:http: //cm.bell-labs.com/who/dmr/chist.html

您仍然可以将按位运算符用作逻辑运算符,其保留的运算符优先级就是证明。在新生儿C上读出位运算符作为逻辑运算符的前世历史

关于证据,我发表了一篇关于比较逻辑运算符和位运算符的博客文章。不言而喻,所谓的位运算符仍然是逻辑运算符,如果您尝试在实际程序中对比它们:http ://www.anicehumble.com/2012/05/operator-precedence-101.html

我还回答了一个与您的问题有关的问题,即 C 中的逻辑运算符有什么意义?

所以这是真的,按位运算符也是逻辑运算符,尽管是短路逻辑运算符的非短路版本。


关于

已经有了逻辑运算符&&、||,那为什么还要用&、|、^呢?

XOR 很容易回答,就像一个单选按钮,只允许一个,下面的代码返回 false。为下面的人为代码示例道歉,认为同时喝啤酒和牛奶是不好的信念已经被揭穿了;-)

String areYouDiabetic = "Yes";
String areYouEatingCarbohydrate = "Yes";


boolean isAllowed = areYouDiabetic == "Yes" ^ areYouEatingCarbohydrate == "Yes";

System.out.println("Allowed: " + isAllowed);

没有等效于 XOR 位运算符的短路,因为需要计算表达式的两边。

关于为什么需要使用&|位运算符作为逻辑运算符,坦率地说,您将很难发现需要使用位运算符(也称为非短路逻辑运算符)作为逻辑运算符。如果您想实现一些副作用并使您的代码紧凑(主观),则逻辑操作可以是非短路的(通过使用按位运算符,也称为非短路逻辑运算符),例如:

while ( !password.isValid() & (attempts++ < MAX_ATTEMPTS) ) {

    // re-prompt

}

上面的代码可以改写如下(去掉括号),仍然和前面的代码有完全相同的解释。

while ( !password.isValid() & attempts++ < MAX_ATTEMPTS ) {

    // re-prompt

}

删除括号,但它仍然产生与括号相同的解释,可以使逻辑运算符的痕迹&更加明显。冒着听起来多余的风险,但我必须强调,没有括号的表达式不会被解释为:

while ( ( !password.isValid() & attempts++ ) < MAX_ATTEMPTS ) {

    // re-prompt

}

综上所述,使用&运算符(更通俗地称为仅按位运算符,但实际上是按位和逻辑(非短路))进行非短路逻辑运算以达到副作用是聪明的(主观的),但不鼓励,这只是为了换取可读性的一种节省效果。

示例来源于此处:非短路逻辑运算符存在的原因

于 2012-07-22T05:45:46.617 回答
0

Java 类型字节是有符号的,这对于按位运算符来说可能是个问题。当负字节扩展到 int 或 long 时,符号位被复制到所有高位以保留解释值。例如:

byte b1=(byte)0xFB; // that is -5
byte b2=2;
int i = b1 | b2<<8;
System.out.println((int)b1); // This prints -5
System.out.println(i);       // This prints -5

原因: (int)b1 在内部是 0xFFFB 而 b2<<8 是 0x0200 所以我将是 0xFFFB

解决方案:

int i = (b1 & 0xFF) | (b2<<8 & 0xFF00);
System.out.println(i); // This prints 763 which is 0x2FB
于 2014-04-14T14:34:58.047 回答