Chapter 4 Making Decisions Flashcards
Making Decisions
Using Operators and Decision Constructs
* Use Java control statements including if, if/else, switch
* Create and use do/while, while, for and for each loops, including nested loops, use break and continue statements
STATEMENTS AND BLOCKS
a Java statement is a complete unit of execution in Java, terminated with a semicolon (;).
a block of code in Java is a group of zero or more statements between balanced braces ({}) and can be used anywhere a single statement is allowed.
// Single statement patrons++; // Statement inside a block { patrons++; }
Block
a block of code in Java is a group of zero or more statements between balanced braces ({}) and can be used anywhere a single statement is allowed.
Java control flow statements.
Java control flow statements.
Control flow statements break up the flow of execution by using decision-making, looping, and branching, allowing the application to selectively execute particular segments of code.
A statement or block often functions as the target of a decision-making statement.
the target of a decision-making statement can be a single statement or block of statements.
// Single statement if(ticketsTaken > 1) patrons++; // Statement inside a block if(ticketsTaken > 1) { patrons++; }
// Single statement if(ticketsTaken > 1) patrons++; // Statement inside a block if(ticketsTaken > 1) { patrons++; }
Note: both of the previous examples are equivalent, stylistically the second form is often preferred, even if the block has only one statement. The second form has the advantage that you can quickly insert new lines of code into the block, without modifying the surrounding structure. While either of these forms is correct, it might explain why you often see developers who always use blocks with all decision- making statements.
THE IF STATEMENT
if (boolean expression) { // Branch if true }
- if keyword
- Parentheses required for boolean expression
- curly braces required for block of multiple statemnts, optional for single statement
Note: it is often considered good coding practice to put blocks around the execution component of if statements, as well as many other control flow statements, although it is certainly not required.
WATCH INDENTATION AND BRACES
if(hourOfDay < 11) System.out.println("Good Morning"); morningGreetingCount++; //will always execute
morningGreetingCount will always execute the increment operation.
Note: Remember that in Java, unlike some other programming languages, tabs are just whitespace and are not evaluated as part of the execution. When you see a control flow statement in a question, be sure to trace the open and close braces of the block, ignoring any indentation you may come across.
THE ELSE STATEMENT
if (boolean expression) { // Branch if true } else { // Branch if false }
- if keyword
- Parentheses required for boolean expression
- curly braces required for block of multiple statemnts, optional for single statement
- optional else statement
else if
if(hourOfDay < 11) { System.out.println("Good Morning"); } else if(hourOfDay < 15) { System.out.println("Good Afternoon"); } else { System.out.println("Good Evening"); }
If neither of the first two expressions is true, it will execute the final code of the else block.
if(hourOfDay < 15) { System.out.println("Good Afternoon"); } else if(hourOfDay < 11) { System.out.println("Good Morning"); // COMPILES BUT IS UNREACHABLE } else { System.out.println("Good Evening"); }
One thing to keep in mind in creating complex if and else statements is that order is important.
If a value is less than 11, then it must be also less than 15 by definition.
Therefore, if the second branch in the example can be reached, the first branch can also be reached. Since execution of each branch is mutually exclusive in this example (that is, only one branch can be executed), then if the first branch is executed, the second cannot be executed.
Therefore, there is no way the second branch will ever be executed, and the code is deemed unreachable.
VERIFYING THAT THE IF STATEMENT EVALUATES TO A BOOLEAN EXPRESSION
the boolean expression inside the if statement is not actually a boolean expression.
ex:
int hourOfDay = 1; if(hourOfDay) { // DOES NOT COMPILE ... }
ex:
int hourOfDay = 1; if(hourOfDay = 5) { // DOES NOT COMPILE ... }
Note: in Java, where 0 and 1 are not considered boolean values.
THE SWITCH STATEMENT
switch statement is a complex decision-making structure in which a single value is evaluated and flow is redirected to the first matching branch, known as a case statement. If no such case statement is found that matches the value, an optional default statement will be called. If no such default option is available, the entire switch statement will be skipped.
switch (variableToTest) { case constantExpression1: // Branch for case1; break; case constantExpression2: // Branch for case2; break; ... default: // Branch for default }
- switch keyword required
- parentheses (required) for variable to test (a switch statement has a target variable that is not evaluated until runtime.)
- begining curly brace (required)
- switch statement may contain 0 or more case branches
- case keyword is required
- Optional break
- Optional default that may appear anywhere within switch statement
- ending curly brace (required)
Proper Switch Syntax
https://github.com/zero14c/OCP-Notes/blob/main/images/structure%20of%20switch%20statement.PNG?raw=true
int month = 5; switch month { // DOES NOT COMPILE case 1: System.out.print("January"); }
does not compile because it is missing parentheses around the switch variable.
int month = 5; switch (month) // DOES NOT COMPILE case 1: System.out.print("January");
does not compile because it is missing braces around the switch body.
int month = 5; switch (month) { case 1: 2: System.out.print("January"); // DOES NOT COMPILE }
does not compile because the case keyword is missing before the 2: label.
Each case statement requires the keyword case, followed by a value and a colon (:).
int month = 5; switch (month) { case 1 || 2: System.out.print("January"); // DOES NOT COMPILE }
does not compile because 1 || 2 uses the short-circuit boolean operator, which cannot be applied to numeric values.
A single bitwise operator (|) would have allowed the code to compile, although the interpretation of this might not be what you expect. It would then only match a value of month that is the bitwise result of 1 | 2, which is 3, and would not match month having a value 1 or 2. You don’t need to know bitwise arithmetic for the exam, but you do need to know proper syntax for case statements.
combine case statements in ways that are not valid.
One last note you should be aware of for the exam: a switch statement is not required to contain any case statements.
For example, this statement is perfectly valid:
switch (month) {}
Switch Data Types
a switch statement has a target variable that is not evaluated until runtime.
Prior to Java 5.0, this variable could only be int values or those values that could be promoted to int, specifically byte, short, char, or int, which we refer to as primitive numeric types.
The switch statement also supports any of the wrapper class versions of these primitive numeric types, such as Byte, Short, Character, or Integer.
Notice that boolean, long, float, and double are excluded from switch statements, as are their associated Boolean, Long, Float, and Double classes.
The reasons are varied, such as boolean having too small a range of values and floating-point numbers having quite a wide range of values. For the exam, though, you just need to know that they are not permitted in switch statements.
When enumeration, denoted enum, was added in Java 5.0, support was added to switch statements to support enum values.
An enumeration is a fixed set of constant values, which can also include methods and class variables, similar to a class definition.
For the exam, you do not need to know how to create enums, but you should be aware they can be used as the target of switch statements.
In Java 7, switch statements were further updated to allow matching on String values.
In Java 10, if the type a var resolves to is one of the types supported by a switch statement, then var can be used in a switch statement too.
SWITCH HISTORY AND CHANGES
Java 12 launched with a Preview release of a
powerful new feature called Switch Expressions, a construct that combines switch statements with lambda expressions and allows switch statements to return a value. You won’t need to know Switch Expressions
for the exam
list of all data types supported by switch statements:
● int and Integer
● byte and Byte
● short and Short
● char and Character
● String
● enum values
● var (if the type resolves to one of the preceding types)
For the exam, we recommend you memorize this list.
Remember, boolean, long, float, double, and each of their associated wrapper classes are not supported by switch statements.
Switch Control Flow
int dayOfWeek = 5; switch(dayOfWeek) { default: System.out.println("Weekday"); break; case 0: System.out.println("Sunday"); break; case 6: System.out.println("Saturday"); break; }
With a value of dayOfWeek of 5, this code will output the following:
Weekday
The first thing you may notice is that there is a break statement at the end of each case and default section. We’ll discuss break statements in more detail when we discuss branching, but for now all you need to know is that they terminate the switch statement and return flow control to the enclosing statement. As you’ll soon see, if you leave out the break statement, flow will continue to the next proceeding case or default block automatically.
Another thing you might notice is that the default block is not at the end f the switch statement. There is no requirement that the case or default statement be in a particular order, unless you are going to have pathways that reach multiple sections of the switch block in a single execution.
Switch Control Flow
var dayOfWeek = 5; switch(dayOfWeek) { case 0: System.out.println("Sunday"); default: System.out.println("Weekday"); case 6: System.out.println("Saturday"); break; }
This code looks a lot like the previous example. Notice that we used a var for the switch variable, which is allowed because it resolves to an int by the compiler. Next, two of the break statements have been removed, and the order has been changed. This means that for the given value of dayOfWeek, 5, the code will jump to the default block and then execute all of the proceeding case statements in order until it finds a break statement or finishes the switch statement:
**Weekday
Saturday
**
The order of the case and default statements is now important since
placing the default statement at the end of the switch statement would cause only one word to be output.
What if the value of dayOfWeek was 6 in this example? Would the
default block still be executed? The output of this example with
dayOfWeek set to 6 would be as follows:
Saturday
Even though the default block was before the case block, only the case block was executed. If you recall the definition of the default block, it is branched to only if there is no matching case value for the switch statement, regardless of its position within the switch statement. Finally, if the value of dayOfWeek was 0, all three statements would be output:
**Sunday
Weekday
Saturday
**
Notice that in this last example, the default statement is executed since there was no break statement at the end of the preceding case block. While the code will not branch to the default statement if there is a matching case value within the switch statement, it will execute the default statement if it encounters it after a case statement for which there is no terminating break statement.
The exam creators are fond of switch examples that are missing break statements!
When evaluating switch statements on the exam, always consider that multiple branches may be visited in a single execution.
Acceptable Case Values
First off, the values in each case statement must be compile-time constant values of the same data type as the switch value.
This means you can use only literals, enum constants, or final constant variables of the same data type.
By final constant, we mean that the variable must be marked with the final modifier and initialized with a literal value in the same expression in which it is declared.
final int getCookies() { return 4; } void feedAnimals() { final int bananas = 1; int apples = 2; int numberOfAnimals = 3; final int cookies = getCookies(); switch (numberOfAnimals) { case bananas: case apples: // DOES NOT COMPILES case getCookies(): // DOES NOT COMPILE case cookies : // DOES NOT COMPILE case 3 * 5 : } }
The bananas variable is marked final, and its value is known at compile-time, so it is valid.
The apples variable is not marked final, even though its value is known, so it is not permitted.
The next two case statements, with values getCookies() and cookies, do not compile because methods are not evaluated until runtime, so they cannot be used as the value of a case statement, even if one of the values is stored in a final variable.
The last case statement, with value 3 * 5, does compile, as expressions are allowed as case values, provided the value can be resolved at compile-time.
They also must be able to fit in the switch data type without an explicit cast. We’ll go into that in more detail shortly.
Next, the data type for case statements must all match the data type of the switch variable.
For example, you can’t have a case statement of type String, if the switch statement variable is of type int, since the types are incomparable.
private int getSortOrder(String firstName, final String lastName) { String middleName = "Patricia"; final String suffix = "JR"; int id = 0; switch(firstName) { case "Test": return 52; case middleName: // DOES NOT COMPILE id = 5; break; case suffix: id = 0; break; case lastName: // DOES NOT COMPILE id = 8; break; case 5: // DOES NOT COMPILE id = 7; break; case 'J': // DOES NOT COMPILE id = 10; break; case java.time.DayOfWeek.SUNDAY: // DOES NOT COMPILE id=15; break; } return id; }
The first case statement, “Test”, compiles without issue since it is a String literal and is a good example of how a return statement, like a break statement, can be used to exit the switch statement early.
The second case statement does not compile because middleName is not a constant value, despite having a known value at this particular line of execution. If a final modifier was added to the declaration of middleName, this case statement would have compiled.
The third case statement compiles without issue because suffix is a final constant variable.
In the fourth case statement, despite lastName being final, it is not constant as it is passed to the function; therefore, this line does not compile as well.
Finally, the last three case statements do not compile because none of them has a matching type of String, the last one being an enum value.
Numeric Promotion and Casting
~~~
short size = 4;
final int small = 15;
final int big = 1_000_000;
switch(size) {
case small:
case 1+2 :
case big: // DOES NOT COMPILE
}
~~~
switch statements support numeric promotion that
does not require an explicit cast.
As you may recall from our discussion of numeric promotion and casting in Chapter 3, the compiler can easily cast small from int to short at compile-time because the value 15 is small enough to fit inside a short.
This would not be permitted if small was not a compile-time constant.
Likewise, it can convert the expression 1+2 from int to short at compile-time. On the other hand, 1_000_000 is too large to fit inside of short without an explicit cast, so the last case statement does not compile.
loop
A loop is a repetitive control structure that can execute a statement of code multiple times in succession.
By making use of variables being able to be assigned new values, each repetition of the statement may be different.
while loop
int counter = 0; while (counter < 10) { double price = counter * 10; System.out.println(price); counter++; }