其他答案已经很好地解释了运算符之间的功能差异,但这些答案几乎适用于现今存在的所有基于 C 的编程语言。问题标记为
java,因此我将尽力回答 Java 语言的具体技术问题。
&
和
|
可以是整数位运算符或布尔逻辑运算符。位运算符和逻辑运算符的语法(
§15.22)如下:
AndExpression:
EqualityExpression
AndExpression & EqualityExpression
ExclusiveOrExpression:
AndExpression
ExclusiveOrExpression ^ AndExpression
InclusiveOrExpression:
ExclusiveOrExpression
InclusiveOrExpression | ExclusiveOrExpression
EqualityExpression
的语法在
§15.21中定义,该部分要求先定义
§15.20中的
RelationalExpression
,而该部分又需要先定义
§15.19和
§4.3中的
ShiftExpression
和
ReferenceType
。其中,
ShiftExpression
需要先定义
§15.18中的
AdditiveExpression
,它继续向下逐层定义基本算术、一元运算符等内容。
ReferenceType
则深入探讨了表示类型的各种方式。(尽管
ReferenceType
不包括原始类型,但最终需要定义原始类型,因为它们可能是数组的维度类型,而数组是一个
ReferenceType
。)
位运算符和逻辑运算符具有以下特性:
- 这些运算符具有不同的优先级,其中
&
优先级最高,|
优先级最低。
- 每个运算符在语法上是左结合的(从左到右分组)。
- 如果操作数表达式没有副作用,则每个运算符都是可交换的。
- 每个运算符都是可结合的。
- 位运算符和逻辑运算符可用于比较两个数值类型的操作数或两个类型为
boolean
的操作数。所有其他情况都会导致编译时错误。
运算符是按位运算符还是逻辑运算符取决于操作数是否“可转换为基本整数类型”(
§4.2),或者它们是否为类型
boolean
或
Boolean
(
§5.1.8)。
如果操作数是整数类型,则对两个操作数执行二进制数值提升(
§5.6.2),使它们都成为操作的
long
或
int
。操作的类型将是(提升后的)操作数的类型。此时,
&
将是按位与,
^
将是按位异或,
|
将是按位或。(
§15.22.1)
如果操作数是
boolean
或
Boolean
,则必要时操作数将经过拆箱转换(
§5.1.8),并且操作的类型将为
boolean
。如果两个操作数都是
true
,
&
的结果将为
true
;如果两个操作数不同,
^
的结果将为
true
;如果任一操作数为
true
,
|
的结果将为
true
。(
§15.22.2)
相比之下,
&&
是“条件与运算符”(
§15.23),而
||
是“条件或运算符”(
§15.24)。它们的语法定义如下:
ConditionalAndExpression:
InclusiveOrExpression
ConditionalAndExpression && InclusiveOrExpression
ConditionalOrExpression:
ConditionalAndExpression
ConditionalOrExpression || ConditionalAndExpression
&&
类似于&
,但只有在左操作数为true
时才评估右操作数。||
类似于|
,但只有在左操作数为false
时才评估右操作数。
条件与具有以下属性:
条件与运算符在语法上是从左到右关联的(从左到右分组)。
条件与运算符在副作用和结果值方面完全关联。也就是说,对于任何表达式a、b和c,计算表达式((a) && (b)) && (c) 产生相同的结果,并以相同的顺序发生相同的副作用,就像计算表达式(a) && ((b) && (c))一样。
条件与运算符的每个操作数必须是布尔类型或Boolean类型,否则将出现编译时错误。
条件与运算表达式的类型始终为布尔类型。
在运行时,首先评估左操作数表达式;如果结果具有Boolean类型,则将其转换为非装箱形式(§5.1.8)。
如果得到的值为false,则条件与运算表达式的值为false,并且不评估右操作数表达式。
如果左操作数的值为true,则评估右操作数表达式;如果结果具有Boolean类型,则将其转换为非装箱形式(§5.1.8)。得到的值成为条件与运算表达式的值。
因此,&& 在布尔操作数上计算的结果与 & 相同。它们的区别仅在于右操作数表达式是有条件地而不是总是被评估的。
"Conditional-Or具有以下属性:"
条件或运算符在语法上是左结合的(从左到右分组)。
条件或运算符对于副作用和结果值来说是完全关联的。也就是说,对于任何表达式a、b和c,计算表达式((a) || (b)) || (c) 产生相同的结果,并以相同的顺序发生相同的副作用,就像计算表达式(a) || ((b) || (c))一样。
条件或运算符的每个操作数必须是类型为boolean或Boolean,否则会出现编译时错误。
条件或表达式的类型始终为boolean。
在运行时,首先评估左操作数表达式;如果结果具有类型Boolean,则它将被转换为非装箱形式。
如果结果值为true,则条件或表达式的值为true,并且不评估右操作数表达式。
如果左操作数的值为false,则评估右表达式;如果结果具有类型Boolean,则它将被转换为非装箱形式。结果值成为条件或表达式的值。
因此,|| 在 boolean 或 Boolean 操作数上计算与 | 相同的结果。它只是在右操作数表达式有条件地而不总是进行评估。
简而言之,正如@JohnMeagher在评论中一再指出的那样,当操作数为
boolean
或
Boolean
时,
&
和
|
实际上是非短路布尔运算符。如果遵循良好的实践(即:没有次要影响),这只是一个小差异。然而,当操作数不是
boolean
或
Boolean
时,这些运算符的行为会非常不同:按位和逻辑运算在Java编程的高级别上根本无法比较。
||
和&&
是短路求值,而|
和&
则是急切求值。 - Hovercraft Full Of Eels&&
和||
,但从未见过&
和|
。如果你要做一些依赖副作用的事情,我不明白为什么会使用类似(a & b | c)
这样的表达式,因为很容易想到“我可以通过使用短路版本来优化这个表达式”。 - Mike Bailey