为什么我们通常使用 || 而不是 |?它们有什么区别?

240

我很好奇为什么我们通常在两个布尔值之间使用逻辑或||而不是位或|,尽管它们都能正常工作。

我的意思是,看看下面这个例子:

if(true  | true)  // pass
if(true  | false) // pass
if(false | true)  // pass
if(false | false) // no pass
if(true  || true)  // pass
if(true  || false) // pass
if(false || true)  // pass
if(false || false) // no pass

我们可以使用|代替||吗?同样的,&&&也一样。


18
大多数人忘记了 | 除了作为位运算符外,还可用作非短路布尔运算符。 - John Meagher
67
它们并不相同。请查看有关它们的教程,特别是有关短路求值与急切求值的部分。||&&是短路求值,而|&则是急切求值。 - Hovercraft Full Of Eels
4
纯好奇,你真的有使用非短路版本的情况吗?我几乎总是看到 &&||,但从未见过 &|。如果你要做一些依赖副作用的事情,我不明白为什么会使用类似 (a & b | c) 这样的表达式,因为很容易想到“我可以通过使用短路版本来优化这个表达式”。 - Mike Bailey
1
这是位运算布尔值与“逻辑”(即与数学“逻辑”有关)之间的区别。逻辑运算符“短路”的事实有些偶然,但非常重要。 - Hot Licks
2
当然,它们具有不同的优先级。 - Hot Licks
显示剩余2条评论
28个回答

3
| is the binary or operator

|| is the logic or operator

2
缺少 | 也是一种非短路布尔运算符。 - John Meagher

3
运算符||&&被称为条件运算符,而|&被称为位运算符。它们的作用不同。
只有左右两侧的表达式均为boolean类型时,条件运算符才会起作用。
位运算符可以处理任何数字操作数。
如果你想进行逻辑比较,请使用条件运算符,因为这样可以为你的代码添加某种类型安全性。

嗯,|&也是条件运算符。请查看我在原帖中的评论中提供的链接。 - Hovercraft Full Of Eels
@Hovercraft Full Of Eels:那个图表有点误导人,它仅在布尔值的上下文中将它们称为条件运算符,其中它们在数学上等同于急切逻辑运算符。当你开始处理除0或1之外的其他值、浮点值或指针等内容时,比较就会失效。 - fluffy
@fluffy:图表中没有任何误导性,因为讨论只涉及布尔运算符。|&可以用作位运算符是完全不同的问题。 - Hovercraft Full Of Eels
1
更准确地说,应该将它们称为用于布尔值的位运算符,而不是布尔运算符。当只有一个位时,它们恰好在数学上等效。 - fluffy

2

一点小提示:Java有 |= 但是没有 ||=

当第一个表达式是用于检测第二个表达式是否会崩溃时,必须使用 ||。例如,在以下情况下使用单个 | 可能会导致NPE。

public static boolean isNotSet(String text) {
   return text == null || text.length() == 0;
}

2

1).(expression1 | expression2),“|”操作符将计算expression2,无论expression1的结果是true还是false。

例子:

class Or 
{
    public static void main(String[] args) 
    {
        boolean b=true;

        if (b | test());
    }

    static boolean test()
    {
        System.out.println("No short circuit!");
        return false;
    }
}

2).(expression1 || expression2)。如果expression1为真,则||运算符不会评估expression2。

例子:

class Or 
{
    public static void main(String[] args) 
    {
        boolean b=true;

        if (b || test())
        {
            System.out.println("short circuit!");
        }
    }

    static boolean test()
    {
        System.out.println("No short circuit!");
        return false;
    }
}

2
其他答案已经很好地解释了运算符之间的功能差异,但这些答案几乎适用于现今存在的所有基于 C 的编程语言。问题标记为 ,因此我将尽力回答 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中的ShiftExpressionReferenceType。其中,ShiftExpression需要先定义§15.18中的AdditiveExpression,它继续向下逐层定义基本算术、一元运算符等内容。ReferenceType则深入探讨了表示类型的各种方式。(尽管ReferenceType不包括原始类型,但最终需要定义原始类型,因为它们可能是数组的维度类型,而数组是一个ReferenceType。)
位运算符和逻辑运算符具有以下特性:
  • 这些运算符具有不同的优先级,其中 & 优先级最高,| 优先级最低。
  • 每个运算符在语法上是左结合的(从左到右分组)。
  • 如果操作数表达式没有副作用,则每个运算符都是可交换的。
  • 每个运算符都是可结合的。
  • 位运算符和逻辑运算符可用于比较两个数值类型的操作数或两个类型为 boolean 的操作数。所有其他情况都会导致编译时错误。
运算符是按位运算符还是逻辑运算符取决于操作数是否“可转换为基本整数类型”(§4.2),或者它们是否为类型 booleanBoolean§5.1.8)。
如果操作数是整数类型,则对两个操作数执行二进制数值提升(§5.6.2),使它们都成为操作的longint。操作的类型将是(提升后的)操作数的类型。此时,& 将是按位与,^ 将是按位异或,| 将是按位或。(§15.22.1
如果操作数是 booleanBoolean,则必要时操作数将经过拆箱转换(§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在评论中一再指出的那样,当操作数为booleanBoolean时,&|实际上是非短路布尔运算符。如果遵循良好的实践(即:没有次要影响),这只是一个小差异。然而,当操作数不是booleanBoolean时,这些运算符的行为会非常不同:按位和逻辑运算在Java编程的高级别上根本无法比较。

1
一个主要的区别在于||和&&表现出"短路",因此只有在需要的情况下才会评估RHS。
例如:
if (a || b) {
    path1...
} else {
    path2..
}

如果a为真,则不会测试b,并执行path1。如果使用|,即使'a'为真,两侧也会被评估。

有关更多信息,请参见此处这里

希望能对您有所帮助。


1
它们之间的基本区别在于 | 首先将值转换为二进制,然后执行位或操作。而 || 不会将数据转换为二进制,只是在其原始状态上执行或表达式。
int two = -2; int four = -4;
result = two | four; // bitwise OR example

System.out.println(Integer.toBinaryString(two));
System.out.println(Integer.toBinaryString(four));
System.out.println(Integer.toBinaryString(result));

Output:
11111111111111111111111111111110
11111111111111111111111111111100
11111111111111111111111111111110

阅读更多: http://javarevisited.blogspot.com/2015/01/difference-between-bitwsie-and-logical.html#ixzz45PCxdQhk


当操作数为布尔值时不正确,并且格式化愚蠢。 - user207421
2
这对我理解为什么Long.valueOf(100 | 200) = 236有帮助。原因如下:0 1 1 0 0 1 0 0 | 1 1 0 0 1 0 0 0 = 1 1 1 0 1 1 0 0 = 128 64 32 0 8 4 0 0 = 236 - donlys

1

非短路运算有其用处。有时候你想要确保两个表达式都被计算。例如,假设你有一个从两个不同列表中删除对象的方法。你可能想要像这样做:

class foo {

    ArrayList<Bar> list1 = new ArrayList<Bar>();
    ArrayList<Bar> list2 = new ArrayList<Bar>();

    //Returns true if bar is removed from both lists, otherwise false.
    boolean removeBar(Bar bar) {
        return (list1.remove(bar) & list2.remove(bar));
    }
}

如果您的方法使用条件操作符,如果第一个列表返回false,则无法从第二个列表中删除对象。
//Fails to execute the second remove if the first returns false.
boolean removeBar(Bar bar) {
    return (list1.remove(bar) && list2.remove(bar));
}

这并不是非常有用的,而且(像大多数编程任务一样)你也可以用其他方法实现它。但它是位运算符的一个使用案例。

1

||通过对两个值进行OR运算返回一个布尔值(这就是为什么它被称为逻辑或)

例如:

if (A || B) 

如果A或B为真,则返回true;如果它们都为假,则返回false。

|是一种运算符,它在两个数值上执行位运算。为了更好地理解位运算,您可以在这里阅读:

http://en.wikipedia.org/wiki/Bitwise_operation


1
当我遇到这个问题时,我创建了测试代码来了解它。
public class HelloWorld{

   public static boolean bool(){
      System.out.println("Bool");
      return true;
   }

   public static void main(String []args){

     boolean a = true;
     boolean b = false;

     if(a||bool())
     {
        System.out.println("If condition executed"); 
     }
     else{
         System.out.println("Else condition executed");
     }

 }
}

在这种情况下,我们只需要改变if条件语句左侧的值,加上a或者b。

|| 的场景,当左侧为true时 [if(a||bool())]

输出 "If condition executed"

|| 的场景,当左侧为false时 [if(b||bool())]

输出-

Bool
If condition executed

结论 || 当使用 || 时,只有在左侧为假时才检查右侧。

| 场景,当左侧为真时 [if(a|bool())]

输出-

Bool
If condition executed

| 当左侧为假 [if(b|bool())]

输出-

Bool
If condition executed

结论:使用 | 时,需检查左右两侧。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接