Java Programming   Java Programming
 |Sofia Home | Content Gallery |
Home
Syllabus
Schedule
Lessons
Assignments
Resources

Lesson 5.3 Logical Operators

In Lesson 5.1, "Relations", you met a set of operators which worked with primitive or object values and produced boolean values. 

In this section, you'll meet another group of operators that can be used to produce boolean values. Unlike the relational operators, however, the logical operators that you'll meet here, only work with boolean operands.

Java has five logical operators. These operators work on boolean values, just like arithmetic operators work on primitive numeric values and like relational operators work on numeric or object values.

The five logical operators include:

  • One unary logical operator: NOT [!]
  • Two versions of the binary AND operator [&&, &]
  • Two versions of the binary OR operator [||, |]

The NOT Operator

The NOT operator--which uses the exclamation point as its symbol, and not the word NOT--appears to the left of [in front of] the value it operates on. The effect of NOT is to reverse the sense of a boolean expression. 

Image shows the NOT operator being used with a boolean operand, producing a boolean value.

For instance, the expression:

10 == 5 + 3 + 2;

is true because 10 is equivalent to 5 + 3 + 2. Using the NOT operator you could write:

!(10 == 5 + 3 + 2);

which would be false. Note that the parentheses are required. If you wrote

! 10 == 5 + 3 + 2;

the expression would not compile, because NOT, being a unary operator, has higher precedence than the arithmetic addition operator [+], or than the relational equality operator [==]. This expressesion attempts to apply the NOT operator to the number 10, but 10 is a numeric value, not a boolean value. NOT only operates on boolean values.

Back to Top

The AND Operators

The four binary logical operators each require two boolean values, one on each side of the operator. The AND symbol used by Java is the ampersand [&] or, more commonly, the double-ampersand [&&]. When using the double-ampersand [&&], make sure you don't put a space or line-break between the symbols--they must appear adjacent to one another.

Image shows the binary boolean operators being used with boolean operands, producing a boolean result.

Because the AND operator is a binary operator, there are four combinations of operands which it can use. The expression on the left may be either true or false, and, for each of these conditions, the epression on the right may also be true or false.

Each of these four possible combinations produces a true or false [that is a boolean] value. If both sides of an AND expression are true, then the entire expression is true. Any other combination is false.

We can present these four combinations in a form known as a truth table. Here is the truth table for the AND operators.

Left Side
Right Side
Result
true
true
true
true
false
false
false
true
false
false
false
false

This corresponds to our normal, logical understanding of the word AND. If a teacher says that you must score at least 85% on the average of three exams AND complete at least 90% of the homework to get an 'A', you understand that you must complete both prerequisites before you'll get an 'A'. Given this scenario, completing 80% of the homework, and getting 100% on the exams would not earn you an 'A'.

When used in programs, the logical AND is used to make sure that more than one condition is true before executing a piece of code. 

Here's a simplistic discounting strategy, for instance, that attempts to provide a bigger discount for products which have sold fewer items, but limits the discounting strategy to low-ticket products:
Using the AND Operator
if (numSold > 0 && totalSales < 1500)
{
  discount = price / 2 / numSold;
}
else
{
  discount = 0.0;
}

Because both sides of the expression must be true before the code will be executed, you won't inadvertantly apply the discount to a new Lexus. You'll also avoid a division-by-zero error that would occur if you didn't check first to make sure that the number sold was greater than zero.

Back to Top

The OR Operators

Like the logical AND operators, the logical OR operators both require two operands, one on the left and one on the right of the operator. Java uses the vertical bar symbol [|], and double vertical bar symbols [||] for its OR operators. As with the AND symbol [&&], make sure you don't put a space between the two vertical bars.

The truth table for the OR operators is different from the AND operators, because the OR operators always produce true values, unless both operands are false. [Remember that the AND operators only produce a true value when both operands are true. All other instances are false.]

Here is the OR truth table:

Left Side
Right Side
Result
true
true
true
true
false
true
false
true
true
false
false
false

As you can see, the top and bottom rows are the same for both AND and OR, while the middle two rows, where one operand is true and the other false, have changed.

The OR operator, like AND, also corresponds to our natural way of speaking. If a teacher promises you ten points for going to the museum or for watching a documentary on PBS, you expect your ten points if you do either one, or if you do both. You won't get any points, however, if you do neither.

When writing code, the OR operator is used to make sure that at least one necessary condition is true. Here's an example that gives a discount to both senior citizens and children:
Using the OR Operator
if (age <= 12 || age >= 65)
{
   discount = .25;
}
else
{
  discount = 0.0;
}

Back to Top

AND and OR Pitfalls

As you begin to write code that uses AND and OR, you're almost certain to occasionally confuse the two. When you do, you'll find yourself writing two kinds of erroneous condition statements: the impossible condition and the unavoidable condition.

Let's take a look at both.

The Impossible Condition

An impossible condition occurs when no conceivable combination of values could ever make the condition true. Let's take our senior-citizen/children's discount example. If you are writing the code, you might receive the problem statement phrased like this:
Give a 25% discount to children who are 12 and under and to patrons who are 65 or older.

If you're in a hurry, you may notice the AND in the problem statement, and write your code like this:

The Impossible Condition
if (age <= 12 && age >= 65)
{
   discount = .25;
}
else
{
  discount = 0.0;
}

This is an impossible condition because for every possible value that the variable age could take on, the expression still evaluates to false. If age is less than 12, then by definition, it cannot be greater than 65. If age is greater than 65, then by definition, it cannot be less than 12. 

Because AND requires that both sides be true before the entire expression is true, this condition can never be true. It is impossible.

Back to Top

The Unavoidable Condition

The impossible condition occurs when you use AND when you should use OR. The unavoidable condition occurs in the opposite case. An unavoidable condition occurs when no conceivable combination of values used as operands could ever make the condition false.

Here's our same discount example. This time an attempt is made to calculate the ticket price, rather than just a discount. The problem statement is phrased this way:

If the patron's age is greater than 12 or less than 65 we'll charge them 8.75 for their ticket. All others will pay the discounted price of 5.75.

If you simply blindly translate this problem into Java code, without thinking, you end up with:

The Unavoidable Condition
if (age > 12 || age < 65)
{
  ticketPrice = 8.75;
}
else
{
  ticketPrice = 5.75;
}

As you can see, for every value of age, the entire expression is true. If age is less than 12, it fails the first test, but passes the second. If age is greater than 65, it passes the first test, while failing the second. If age falls between 12 and 65 it passes both tests.

For every possible value of age, the expression is true. Everyone pays full price because the condition is unavoidable.

Back to Top

A Solution

The solution is simply to double-check each time you use AND and OR to make sure you are using the correct one. Our first example can be fixed by using OR instead of AND, like this:

Fixing the Impossible Condition
if (age <= 12 || age >= 65)
{
   discount = .25;
}
else
{
  discount = 0.0;
}

In the same way, the unavoidable condition can be fixed by replacing OR with AND, like this:
Fixing the Unavoidable Condition
if (age > 12 && age < 65)
{
  ticketPrice = 8.75;
}
else
{
  ticketPrice = 5.75;
}

Short-circuit Evaluation

If you've ever watched the television game show "Wheel of Fortune", you know that there comes a time when the answer to the puzzle is obvious. 

The same thing is true with Java's logical operators; at some point, Java "knows" what the result will be, even if it hasn't evaluated the entire expression.

Let's look at a couple of examples:

if ( 2 + 2 == 5 && a > b)
{
  doAVeryUnlikelyThing();
}

Since you know that the expression 2 + 2 == 5 is false, it really doesn't matter if a is larger than b or not. You know that the whole expression is false, regardless of the values stored in a and b. 

Here's another example:

if (a == a || c == d)
{
  aSureBet();
}

Again, no matter what values are stored in c or d, you know that the whole expression is true, because a == a by definition. You don't have to even glance twice at c and d.

This is how Java's && and || operators work:

  • The && and || operators evaluate their expressions from left to right and stop evaluation when the result is assured.
  • For &&, the evaluation stops when a false value is encountered.
  • For ||, the evaluation stops when a true value is encountered

The & and | operators, in contrast, evaluate both sides of an expression, even if the result is a forgone conclusion.

The "Bitwise" Logical Operators
If you are a C or C++ programmer, you are probably used to thinking of the & and | operators as the "bitwise" AND and OR operators. In Java, however, these operators live a double life. When applied to numeric values, they act like the bitwise logical operators used in C; when applied to boolean operands, they act as described here.
Back to Top

Short Circuit/Long Circuit

Here are some examples using the different operators, along with a little commentary.
Example 1

Here's an example that checks to make sure a is not equal to zero and that b divided by a is greater than twelve. 

if ( (a != 0) && ( b / a > 12 ))
{
  performExcellently();
}

Because the short-circuit form of AND [&&] is employed, if a is equal to zero, the second test will never be attempted, guaranteeing that there will be no unsightly divide-by-zero error.

Example 2

This is the same as the previous example, except the "long-circuit" version of AND is used. What result would you expect?

if ( (a != 0) & ( b/a > 12))
{
  performPoorly();
}

While using & instead of && doesn't change the ultimate result of the expression, this example nevertheless is in error. If a is ever zero, then a divide-by-zero is guaranteed, because both sides of the AND expression will be evaluated.

A bug like this is hard to find in testing because you must make sure to test it when a is zero. This test is often skipped.

Example 3

The OR operator is also suceptible to certain pitfalls, along with the AND operator. Because of  short-circuit evaluation,  the right-hand side of the following expression is not evaluated when a is greater than ten. That means that the variable b will only be incremented when a is less than or equal to ten.

if ( (a > 10) || (b++ > 7))
{
  aPotentialProblem();
}

Unlike the previous example, this is not a definite bug. Nonetheless it is very poor style because those reading it will have a hard time accounting for the side effect that occurs when the variable b is incremented.

Example 4

This shows a more understandable way to code the previous example. Here, the variable b is incremented every time the expression is evaluated, and it is much easier to follow the execution of the code when you are debugging.

if ( (a > 10) | (b++ > 7))
{
  allsOK();
}
Back to Top

Precedence

In Lesson 3, "Primitives", you learned about precedence and its effect on the evaluation of numeric expressions. Precedence has an effect on the use of relational and logical operators as well. Here is an updated precedence chart that includes the operators you've used so far. [click image to enlarge]

Image shows the precedence table for each operator.

Note that the relational operators all appear below the arithmetic operators, so you can write

a > 3 + 5

rather than

a > (3 + 5)

Note also that AND [&&] has higher precedence than OR [||]. This means that the expression:

10  < 15 || 5 > 8 && 3 > 5

is evaluated as 

10 < 15 || ( 5 > 8 && 3 > 5)

which is true, instead of as

(10 < 15 || 5 > 8) && 3 > 5

which is false.

Something to Talk About

Do you understand boolean expressions? Here are some expressions to evaluate to make sure:

What is the value of x, if, initially, a == 3, b == 6, c == 9, and x = 1? 
Evaluate each expression independently.

  1. if ( a <= b ) x += c;
  2. if ( a == b && c == 9 ) x = 4;
  3. if ( a == b || c == 9 ) x = 4;
  4. if ( ! (a == b) || c == 9) x = 7;
  5. if ( ! (a == b || c == 9)) x = 7;

Please continue to the next section of this lesson.

 

Back to Top

 

Content Developed by Stephen Gilbert, Licensed under a Creative Commons License
Published by the Sofia Open Content Initiative
© 2004 Foothill-De Anza Community College District &The William and Flora Hewlett Foundation