Back to Blog
Java

Java || vs |: Logical and Bitwise OR Explained

java || vs |: Understand the difference between || and | in Java, including short-circuit behavior, bitwise semantics, and when to use each.

Java operatorsshort-circuit evaluationbitwise operationsconditional logicboolean expressions
A split diagram showing Java's logical OR with short-circuit alongside bitwise OR, illustrating the difference between boolean and integer contexts.

When you write java || vs | into a search engine, you are usually staring at a line of code where a single character changes program behavior. The difference is not just stylistic: || is a conditional-or operator, while | is a bitwise-or operator. In boolean contexts, | can be used as a non-short-circuit logical OR, but it behaves differently in numeric contexts. Understanding this distinction matters because it affects control flow, performance, and correctness.

The Basic Difference: Short-Circuit vs Full Evaluation

The most important difference between || and | is short-circuiting. When you use ||, Java evaluates the left-hand operand first. If it evaluates to true, the right-hand operand is never evaluated, because the overall expression is already known to be true. This is called short-circuit evaluation.

With |, both operands are always evaluated, regardless of the value of the left operand. The expression a | b evaluates both a and b, and then performs a bitwise OR on their values. If the operands are booleans, the result is the logical OR of the two, but without short-circuiting.

Consider this example:

boolean first() { System.out.println("first"); return true; } boolean second() { System.out.println("second"); return false; } boolean result1 = first() || second(); // prints "first", result1 is true boolean result2 = first() | second(); // prints "first" and "second", result2 is true

In the first line, second() is not called because first() already returned true. In the second line, second() is called even though the result is unnecessary. This can have side effects, performance implications, or even cause exceptions if the second operand has unwanted effects.

Bitwise OR on Integers

When applied to integer types (int, long, short, char, byte), | performs a bitwise OR. It combines the bits of the two values. For example:

int flags = 0b1010; int mask = 0b1100; int result = flags | mask; // result is 0b1110

Here, | sets a bit in the result if the bit is set in either operand. This is a common pattern for combining flag values, as seen in java.nio.file.StandardOpenOption or java.awt.event.InputEvent masks.

The || operator does not work on integers. It only accepts boolean expressions. Attempting to use || with integer operands results in a compile-time error.

Boolean Context: | as Non-Short-Circuit Logical OR

In a boolean expression, | behaves like a logical OR but without short-circuiting. This can be useful when you need both sides to be evaluated, for example, to ensure that some side effect occurs on both sides.

if (checkLeft() | checkRight()) { // do something }

This evaluates checkRight() even if checkLeft() returned true. In contrast, || would skip checkRight() in that case.

However, this is rarely recommended. In most situations, short-circuiting is desirable because it can avoid unnecessary work and prevent errors. A common pattern is to use || for control flow and & or | only when you explicitly need to evaluate both operands.

Practical Use Cases: When | Is Justified

There are a few legitimate use cases for the non-short-circuit | with booleans.

Side Effects Required on Both Sides

If you have two independent operations that both mutate state and you need to know whether at least one succeeded, you might want to evaluate both:

boolean changed = updateCache() | notifySubscribers();

If updateCache() returns true but notifySubscribers() must still be called, || would skip the second call. However, such code is often harder to read and maintain. A clearer approach may be to evaluate both explicitly:

boolean cacheUpdated = updateCache(); boolean notified = notifySubscribers(); boolean changed = cacheUpdated | notified;

This makes the intent explicit and avoids the implicit non-short-circuit behavior.

Bitmask Operations

For bitmask operations, | is the correct operator. For example, when opening a file with multiple options:

Files.newBufferedWriter(path, StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.APPEND);

Internally, these options are combined using bitwise OR. You might also use | directly when constructing flags for a method that takes an int parameter.

Operator Precedence and Associativity

Both || and | are binary operators. | has higher precedence than ||. This means that in an expression without parentheses, | is evaluated before ||. Also, | is left-associative, so expressions are evaluated from left to right.

Consider this expression:

boolean result = a || b | c;

Because | has higher precedence, it is evaluated as a || (b | c). If a is true, the entire expression is true, and b | c is not evaluated due to short-circuiting. If a is false, then b | c is evaluated, and the result is the boolean OR of b and c (with both evaluated). This can lead to subtle bugs if you assumed left-to-right evaluation without precedence.

When mixing && and ||, the precedence order is: && higher than ||. This is a common source of confusion. Always use parentheses when mixing logical operators if the intended grouping is not obvious.

Type Compatibility: When Each Operator Compiles

|| requires both operands to be boolean (or Boolean with unboxing). | works with boolean types and also with integral types. Using | with booleans is valid, but using || with integers is not.

| Operand type | || | | | | --- | --- | --- | | boolean | Yes, short-circuit | Yes, no short-circuit | | int, long | No | Yes, bitwise | | short, char, byte | No | Yes, bitwise (after promotion) |

For Boolean objects, both operators unbox them to primitive boolean before evaluation. However, if a Boolean is null, a NullPointerException occurs during unboxing, regardless of the operator used.

Performance Considerations

Performance differences between || and | are negligible in ordinary code. The JVM can optimize boolean expressions, and short-circuiting often saves work that would otherwise be wasted. The main performance concern is avoiding unnecessary calls to methods that are expensive or that have side effects.

If your right-hand operand is a costly computation, || can save time by skipping it when not needed. For example:

if (isCached() || loadFromDisk()) { // use the data }

If isCached() returns true, the expensive loadFromDisk() is not invoked. Using | here would always load from disk, which could be a significant performance hit in a hot path.

Common Mistakes and Pitfalls

A frequent mistake is using | when || is intended, especially when the operands are method calls. This can lead to unexpected side effects or performance problems. Another common mistake is using || with integer values, which causes a compile error.

Some developers use | in boolean expressions to guarantee both sides execute, but this often indicates unclear intent. It is better to refactor such code to make the evaluation order explicit.

Another pitfall is forgetting operator precedence when mixing &, |, &&, and ||. For example:

boolean b = x == 1 | y == 2;

This is valid because == has higher precedence than |. However, x == 1 || y == 2 is equivalent in result but short-circuits if x is 1. The difference matters if y is a method call or an expression with side effects.

When | Is Required for Bitwise Logic

The only situation where | is irreplaceable is when you are performing bitwise operations on integers. For example, combining permission flags:

int read = 1 << 0; int write = 1 << 1; int execute = 1 << 2; int allowed = read | write;

To check if a particular permission is set, you would use &:

if ((allowed & read) != 0) { // can read }

This pattern appears in legacy code, network protocols, and certain configuration APIs. If you are working with such APIs, | is the correct tool. For boolean logic, stick with || unless you have a specific reason to force both evaluations.

Understanding the NullPointerException Risk

When using reference Boolean values, both || and | unbox the operands. If a Boolean is null, unboxing throws NullPointerException. This is not special to these operators; any unboxing operation does the same. But it is worth noting that short-circuiting can hide a null "reference" on the right side:

Boolean a = null; Boolean b = true; boolean result = b || a; // no exception, a is not unboxed boolean result2 = b | a; // NullPointerException, a is unboxed

In the first line, b is true, so a is never unboxed. In the second line, a is unboxed, triggering an exception. This is a concrete example of how | forces full evaluation and why it can cause failures that || would avoid.

Final Section: Choosing Between || and | in Maintainable Code

In production code, the default choice should be || for boolean conditions. It is the standard operator, it short-circuits, and it is immediately understood by other developers. If you need bitwise combination of integer flags, use |. Using | with booleans is rare and should be accompanied by a comment explaining why both operands must be evaluated.

When reviewing code that uses | with booleans, check whether short-circuiting would change behavior. If both sides must always execute, consider extracting them into separate statements or methods to make the requirement obvious. This improves readability and reduces the chance of someone later "optimizing" the code by replacing | with ||, which might break the intended side effects.

Ultimately, the choice between || and | is governed by semantics: || is for boolean logic with short-circuiting, | is for bitwise operations or explicit full evaluation of boolean operands. Keeping that distinction clear in your code helps avoid subtle bugs and keeps the intent readable.

java || vs |: Practical Usage and Code Examples | RYUSLOG DEV