5. 0101 - Decisions
What’s the Point?
-
Understand Boolean expressions
-
Implement
if
andif-else
statements -
Use
else if
and nestedif-else
statements -
Apply decision structures to object-oriented programming
Source code examples from this chapter and associated videos are available on GitHub.
Decisions, or branching gives our programs the ability to execute different code depending on what’s happening as it runs.
At the core of decisions is the concept of Boolean logic, which is named after a 19th-century mathematician named George Boole.
Boolean logic is the foundation of the if
statements that will be the foundation of our branching code.
5.1. Boolean Expressions
Until now, our programs have all executed very sequentially and predictably—one line of code after another. How boring! To give our programs the ability to branch and execute different code based on different conditions, we need to introduce the concept of decisions.
In computer programming, a decision is made based on a Boolean expression, which is an expression that evaluates to either true
or false
.
Think of them as questions that can be answered with a simple "yes" or "no".
Is this student’s GPA high enough to qualify for the scholarship? Has this cell phone customer used all of their data? Did the user type "exit"?
The true
or false
value of a Boolean expression can be used to determine which code block will execute next, so it’s important to understand how they work.
5.1.1. Boolean Values
The simplest way to use Boolean in Java is with the keywords true
and false
.
These can be assigned to variables of type boolean
:
1
2
boolean isTimAmazing = true;
boolean isClassBoring = false;
5.1.2. Relational Operations
Also known as comparison operations, relational operations are expressions that compare two values.
You will remember them from math class. Is x
greater than y
? Is a
less than or equal to 10
? Is c
equal to d
?
Relational operators, like arithmetic operators, are binary operators that require two operands.
In other words, we need two values to compare.
In our math class, we could draw symbols that aren’t on our keyboard, like a ≥ for "greater than or equal to."
In Java, where we have to stick with stuff on our keyboard, we use the following symbols:
Operator |
Description |
|
Equality (checks if two values are equal) |
|
Inequality (checks if two values are not equal) |
|
Less-than |
|
Greater-than |
|
Less-than-or-equal-to |
|
Greater-than-or-equal-to |
A relational operation will always evaluate to either true
or false
.
We can store that result in a boolean
variable, as shown below, and we’ll also learn how to use these expressions in if
statements later in this chapter.
1
2
boolean canBuyAlcohol = age >= 21;
boolean isNegative = number < 0;
These assignment statements work like any other: the expression to the right of the equals symbol is evaluated, and the result is stored in the variable on the left.
5.1.3. Testing a String for Equality
In Java, we can’t use the ==
operator to compare two strings.
The following code will not work as we might expect:
String
comparison.1
2
3
4
// Should output "true", but (sometimes) doesn't
String name = "Paul McCartney";
System.out.print("Is this person Paul McCartney?");
System.out.println(name == "Paul McCartney"); // DON'T DO THIS!
The ==
operator does not compare the contents of the strings, but rather the memory addresses where the strings are stored.
Due to the nuances of how the Java runtime handles String
objects, this code will work sometimes, by coincidence, but it’s not reliable.
Instead, we’ll need to use the equals()
method, which is a method of the String
class and gets called using dot notation.
This method will examine the contents of the strings and return true
if they are the same, and false
if they are different.
String
comparison.1
2
3
4
// Outputs "true"
String name = "Paul McCartney";
System.out.print("Is this person Paul McCartney? ");
System.out.println(name.equals("Paul McCartney"));
The equals() method is case-sensitive, so "Paul McCartney" is not the same as "paul mccartney" . The String class also has a equalsIgnoreCase() method that will compare two strings without regard to case.
|
5.2. if
Statements
The if
statement is the most basic decision-making structure in Java.
It allows us to execute a block of code only if a certain condition is true.
The syntax of an if
statement is the keyword if
, followed by a Boolean expression in parentheses, followed by a block of code in curly braces.
If the Boolean expression evaluates to true
, the block of code will execute.
If the Boolean expression evaluates to false
, the block of code will be skipped.
In either case the program will continue executing the next line of code after the if
-block.
if
Statement.1
2
3
4
5
int age = 20;
if (age < 21) {
System.out.println("You can't buy alcohol.");
}
System.out.println("Keep that in mind when you go to the store!");
In this example, the if
statement checks if the variable age
is less than 21
.
Since 20
is less than 21
, the Boolean expression evaluates to true
, and the block of code inside the if
statement is executed—and it prints "You can’t buy alcohol."
The program then continues to the next line of code, which prints "Keep that in mind when you go to the store!"
If the value of age
were 22
, the Boolean expression would evaluate to false
, and the block of code inside the if
statement would be skipped.
The program would then continue to the next line of code, which prints "Keep that in mind when you go to the store!"
Keep in mind, the parentheses after the if
keyword can contain any Boolean expression—not just this simple example.
5.3. Adding an else
Block
An if
statement simply determines whether or not to execute a single block of code.
If we want to choose between two blocks of code, we can add an else
block to the if
statement.
The syntax is simple: after the if
block, add the keyword else
, followed by a block of code in curly braces.
if-else
Statement.1
2
3
4
5
6
7
8
int age = 20;
if (age < 21) {
System.out.println("You can't buy alcohol.");
}
else {
System.out.println("You can buy alcohol.");
}
System.out.println("Keep that in mind when you go to the store!");
An if-else
statement will always execute one block of code or the other, but never both.
Basically, it’s an either-or situation.
5.4. The if-else if
Structure
The if-else
structure is great for choosing between two blocks of code, but what if we have more than two options?
To handle this, we can chain multiple if-else
statements together.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class WeatherRecord {
// Fields
private String date;
private int highTemperature;
private double averageWindSpeed;
public String getTempDescription() {
if (this.highTemperature > 90) {
return "Hot";
} else if (this.highTemperature > 70) {
return "Warm";
} else if (this.highTemperature > 50) {
return "Cool";
} else {
return "Cold";
}
}
}
In this example, the getDescription()
method will return a String
that describes the weather based on the high temperature of the day.
-
If the high temperature is greater than
90
, the method will return "Hot". -
If the high temperature is greater than
70
, the method will return "Warm". -
If the high temperature is greater than
50
, the method will return "Cool". -
If the high temperature is
50
or less, the method will return "Cold".
The code begins with the first statement, and if it evaluates to true
, the corresponding block of code will execute.
If the first statement evaluates to false
, the program will move on to the next else if
statement, and so on.
Once a code block is executed, it will hit a return
statement, which will exit the method and not evaluate any other blocks of code.
Therefore, only one block of code will execute.
If the program gets through the entire structure without finding a true
condition, it will execute the block of code in the else
block, if one is present.
If we’re looking at that code critically, we might notice that the else
block is not strictly necessary.
We could just put the return "Cold";
statement at the end of the method, and it would work the same way.
However, that depends on the logic of the if-else if
structure and whether or not we’re using return
statements in the blocks of code.
In summary, An if-else if
structure can execute, at most, one block of code.
If an else
block is included at the end, it guarantees that exactly one block of code will execute.
5.5. Nested if-else
Statements
If we want a block of code to execute only if two different conditions are met, we can place if
statements inside of each other—which is called nesting.
Nested if statements check multiple conditions in a hierarchical way: if one condition is met, it will proceed and check the next condition; if the first condition is not met, it will skip the inner if
block.
if-else
statement. 1
2
3
4
5
6
7
8
9
10
11
if (condition1) {
if (condition2) {
// executes if both condition1 and condition2 are true
}
else {
// executes if condition1 is true and condition2 is false
}
}
else {
// executes if condition1 is false
}
In the example below, the outermost if-else structure checks the high temperature of the day. The if-else structures within those blocks check the average wind speed and return an appropriate description.
WeatherRecord.java
(excerpt). Example of a nested if-else
statement. See GitHub for the complete file. 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public String getFullDescription() {
if (this.highTemperature > 90) {
if (this.averageWindSpeed > 10) {
return "Hot and Windy";
} else {
return "Hot";
}
} else if (this.highTemperature > 70) {
if (this.averageWindSpeed > 10) {
return "Warm and Windy";
} else {
return "Warm";
}
} else if (this.highTemperature > 50) {
if (this.averageWindSpeed > 10) {
return "Cool and Windy";
} else {
return "Cool";
}
} else {
if (this.averageWindSpeed > 10) {
return "Cold and Windy";
} else {
return "Cold";
}
}
}
5.6. Using Logical Operators
In addition to the relational operators, Java also includes logical operators we can use to make more complex Boolean expressions. A logical operator is a binary operation, so it takes two operands—but the operands are Boolean expressions instead of numbers.
Operator |
Name |
Description |
|
AND |
Evaluates to |
|
OR |
Evaluates to |
|
NOT |
Evaluates to |
These operators can be used to combine multiple Boolean expressions into a single, more complex expression. For example, we could check if a student is eligible for a scholarship based on both their GPA (3.5 or better) and their age (younger than 25).
1
2
3
if (gpa >= 3.5 && age < 25) {
System.out.println("You qualify for the scholarship!");
}
In this example, the &&
operator is used to combine two Boolean expressions.
The if
statement will only execute the block of code if both expressions are true
.
Often, the logic we create using an AND operation can be implemented using nested if-else statements, and vice versa. |
The OR operation is similar, but only one of the expressions needs to be true
for the entire expression to be true
.
1
2
3
4
5
6
boolean isTimAmazing = false;
boolean isClassFun = true;
if (isTimAmazing || isClassFun) {
System.out.println("You should take this class!");
}
Both operands in an AND or OR operation have to be complete Boolean expressions.
Put another way, each side of the &&
or ||
operator must be able to evaluate to true
or false
on its own.
The following code is a very common beginner mistake and will not compile:
if (percentage >= 80 && < 90) { … }
This reads like "if the percentage is greater than or equal to 80 and less than 90," but the second part of the expression is not a complete Boolean expression.
We need to include the variable name and the comparison operator on both sides of the &&
operator.
The NOT operation is a little different, as it only takes one operand (making it a unary operator_, if you’re nerdy about words, like I am).
It simply inverts the value of the operand.
If the operand is true
, the NOT operation will evaluate to false
.
If the operand is false
, the NOT operation will evaluate to true
.
1
2
3
4
5
boolean isTimAmazing = false;
if (!isTimAmazing) {
System.out.println("At least his mom still loves him!");
}
5.6.1. Range Checking
There are a lot of situations where we might need to combine multiple conditions to make a decision, but one of the most common is range checking. Range checking means we want to see if a value is within a certain range.
A common example of range checking is to convert a percentage grade to a letter grade.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public String getLetterGrade(int percentage) {
if (percentage >= 90 && percentage <= 100) {
return "A";
} else if (percentage >= 80 && percentage < 90) {
return "B";
} else if (percentage >= 70 && percentage < 80) {
return "C";
} else if (percentage >= 60 && percentage < 70) {
return "D";
} else if (percentage >= 0 && percentage < 60) {
return "F";
} else {
return "Invalid percentage";
}
}
The AND operator &&
used in this example means that in order to return "B"
, for example, the percentage must be greater than or equal to 80
and less than 90
.
If either of those conditions is not met, the program will move on to the next else if
statement.
5.7. switch
Statements
Java includes a structure called a switch
statement that can be used to choose between multiple options.
It is essentially another way to write an if-else if
structure, but it can be more readable and easier to write in some situations.
I generally consider switch
structures to be optional—you can complete all of the assignments in this course without using them—but they are a useful tool to have in your programming toolbox.
And since you see them often in code written by others, it’s good to know how they work.
The basic structure of a switch
statement is as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
switch (expression) {
case value1:
// Code to be executed if expression equals value1
break;
case value2:
// Code to be executed if expression equals value2
break;
case value3:
// Code to be executed if expression equals value3
break;
default:
// Code to be executed if expression doesn't match any case
}
The expression
in the parentheses after the switch
keyword is evaluated, and then the program will jump to the case
that matches the value of the expression.
If there is no match, the program will execute the default
block, if it is present.
The break
statement is used to exit the switch
block due to a behavior of switch
that can be confusing to beginners, known as fall-through.
If we don’t include a break
statement at the end of a case
block, the program will continue executing the code in the next case
block, even if the value of the expression doesn’t match the case
.
This can be useful in some situations, but it’s generally not what you want, so you’ll usually see a break
statement at the end of each case
block.
switch
statement. 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void trafficInstructions(String lightColor) {
switch (lightColor) {
case "red":
System.out.println("Stop!");
break;
case "yellow":
System.out.println("Slow down!");
break;
case "green":
System.out.println("Go!");
break;
default:
System.out.println("Invalid light color.");
}
}
5.8. Solution Walkthrough
In "solution walkthrough" videos, I give a problem/prompt that is similar to the kinds of work I assign, and then I record myself writing a solution. It’s not absolutely mandatory to watch this video, but students report that these videos are particularly helpful.
Check Yourself Before You Wreck Yourself (on the assignments)
Can you answer these questions?
Sample answers provided in Stuff That’s Tacked On The End.