Making Decisions

Creating making-decisions statements

Statements and Blocks

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.

patrons++; // single statement

// statement inside a block
{ 
	patrons++;
}

A statement or block often functions as the target of a decision-making statement

if(ticketsTaken > 1)
patrons++; // single statement

// statement inside a block
if(ticketsTaken > 1)
{ 
	patrons++;
}

The if statement

It executes a code of block only under certain circumstances.

One area where the exam writers will try to trip you up is on if statements without braces {}. The following example will print the statement only if the condition is met, but it will always execute the increment operation.

if(hourOfDay < 11)
	Sytem.out.println("Good Morning");
	morningGreetingCount++;

The else (if) statement

The order is important.

if(hourOfDay < 15) {
	System.out.println("Good Afternoon");
} else if(hourOfDay < 11) {
	// UNREACHABLE CODE
	System.out.println("Good Morning");
} else {
	System.out.println("Good Evening");
}

(!) Verify also, that the boolean expression inside the if statement, is actually a boolean expression.

int hourOfDay = 1;
if(hourOfDay) { // DOES NOT COMPILE
}

1 and 0 are not considered valid booleans.

int hourOfDay = 1;
if(hourOfDay = 5) { // DOES NOT COMPILE
}

switch statement

A single value is evaluated and flow is redirected to the first matching case. If no such statement is found that matches the value, an optional default statement will be called. If no such default option is available, the entire switch will be skipped.

proper switch syntax

the exam may present invalid switch syntax to see whether you’re paying attention.

int month = 5;

switch month { // DOES NOT COMPILE. NO PARENTHESIS.
	case 1: sout("january");
}

switch (month) // DOES NOT COMPILE. NO BRACKETS.
	case 1: sout("january");
	
switch (month) {
	case 1: 2: sout("january"); // DOES NOT COMPILE
}

switch (month) {
	case 1 || 2: sout("january"); // DOES NOT COMPILE
}

switch(month) { // COMPILES. perfectly valid. 
}

switch data types

The switch’s target variable is evaluated at runtime.

The following data types are permitted: int, byte, short, char, enum values, String, var (if its type is supported). Also the wrapper classes: Integer, Byte, Character… The following data types are not allowed in a switch: boolean, long, float, double.

var dayOfWeek = 5;
switch(dayOfWeek) {
	case 0:
		sout("sunday");
	default:
		sout("weekday");
	case 6:
		sout("saturday");
	break;
}
// weekday
// saturday

The default block doesn’t need to be at the end of the statement.

var dayOfWeek = 0;
switch(dayOfWeek) {
	case 0:
		sout("sunday");
	default:
		sout("weekday");
	case 6:
		sout("saturday");
	break;
}
// sunday
// weekday
// saturday

At the last example, it would also jump to the default value as there’s no break keyword.

At the exam, watch your for switch examples that are missing the break statement!

Acceptable case values

Not just any variable or value can be used in a case statement. The values must be compile-time constant values of the same data type. This means only literals, enums or final constant variables of the same type.

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 COMPILE;
		case getCookies(): // DOES NOT COMPILE;
		case cookies: // DOES NOT COMPILE;
		case 3 * 5;
	}
}

This is valid, because it’s a final constant variable.

final int bananas = 1;
[...]
case bananas:

This is valid. It’s possible as expressions are allowed, provided the value can be resolved at compile-time. They also must be able to fit without an explicit cast.

case 3 * 5:

This is not valid, because it’s not final.

int bananas = 1;
[...]
case bananas: // DOES NOT COMPILE

This is not valid. It’s not evaluated until runtime. Cannot use methods.

final int bananas = getBananas();
[...]
case bananas: // DOES NOT COMPILE

This is not valid. It’s not evaluated until runtime. Cannot use methods.

case getBananas(): // DOES NOT COMPILE

A switch statement must have the same data type.

private void test(String firstName) {
	switch(firstName) {
		case 'J': // DOES NOT COMPILE. CHAR IS NOT STRING.
			break;
	}
}

Numeric Promotion and Casting

Switchs support numeric promotion that does not require an explicit cast

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
}

The compiler can easily cast from int to short at compile-time if a value is small enough to fit inside a short. If a number is too big, it wouldn’t compile.

while loops

One thing to remember is that a while loop may terminate after its first evaluation.

int full = 5;
while(full < 5) {
	// this would never print
	sout("Not full!");
	full++;
}

do / while statement

It guarantees that the statement or block will be executed at least once.

int lizard = 0;
do {
	lizard++;
} while(false);
sout(lizard); // 1

infinite loops

You have to make sure they always terminate.

int pen = 2;
int pigs = 5;
while(pen < 10) // infinite loop
	pigs++;

for loops

Variables declared in the initialization block of a for loop have limited score and are accessible only within the for loop. Be wary of this at the exam.

for(int i = 0; i < 10; i++)
	sout("value is: " +i);
sout(i); // DOES NOT COMPILE

var in a loop

Since Java10, you may now use var in a for loop. The compiler treats it as having an int.

for (var counter = 5; counter > 0; counter--) {
	sout(counter + " ");
}

For the exam you have to be able to read forward and backward loops. Pay atention to the decrement operator -- and the condition. < is not the same as <=. If you see a for loop with a decrement operator, you should assume they’re trying to test your knowledge of loop operations.

Working with for loops

Although most for loops are well defined, there’re five variations and edge cases you could see in the exam.

1. Creating an infinite loop
for( ; ; ) // COMPILES
	sout("Hello world!");

The semicolons are required. Without it wouldn’t compile.

for ( ) // DOES NOT COMPILE
	sout("Hello world!");
2. Adding multiple terms to the for
int x = 0;
for(long y = 0, z = 4; x < 5 && y < 10; x++, y++) {
	sout(y + " "); }
sout(x + " ");

You can declare a variable, such as x, before the loop begins and use it after it completes. The update statement can modify multiple variables.

3. Redeclaring a variable in the initialization block
int x = 0;
for(int x = 4; x < 5; x++) { // DOES NOT COMPILE
	sout(x + " ");
}

This does not compile because x is trying to be initialized twice and results in the compiler stopping because of a duplicate variable declaration.

We can fix this by removing the declaration of x from the for loop

int x = 0;
for(x = 0; x < 5; x++) {
	sout(x + " ");
}
4. Using incompatible data types in the initialization block
int x = 0;
for(long y = 0, int z = 4; x < 5; x++) { // DOES NOT COMPILE
	sout(y + " ");
}

The variables in the initialization block must all be of the same type. If both y and z were both long, the could would’ve compiled without issue.

5. Using loop variables outside the loop
for(long y = 0, x = 4; x < 5 && y < 10; x++, y++) {
	sout(y + " ");
}
sout(x); // DOES NOT COMPILE

This is important. If you notice x is defined in the initialization block of the loop, and then used after the loop terminates. Since x was only scoped for the loop, it will cause a compiler error.

Modifying loop variables

for(int i=0; i<10; i++) { // INFINITE LOOP
	i = 0;
}

Java does let you modify loop varaibles, whether they be in for, while, or do/while loops.

for(int k=0; k<10; ) // COMPILES. IT LOOPS 10 TIMES.
	k++;

for-each loop

public void printNames(String[] names) {
	for(String s : names) {
		sout(s);
	}
}

It’s designed to iterate over arrays and various Collection Framework classes.
The right side has to be one of the following:

  • An array
  • An object which implements from java.lang.Iterable

This does not inclue Map. It cannot be used in a for-each loop.

It also accepts a var.

public void printNames(String[] names) {
	for(var s : names) {
		sout(s);
	}
}

For the exam, when you see a for-each loop, make sure the right side is an array or Iterable object and the left side has a matching type.

String[] names = new String[3];
for(int name : names) { // DOES NOT COMPILE
	sout(name + " ");
}

Notice, the array is initialized with three null pointer values.

Controlling Flow with Branching

Other ways loops could end or branch

Nested Loops

int[][] myComplexArray = { {5,2,1,3}, {3,9,8,9}, {5,7,12,700} };

for(int[] mySimpleArray : myComplexArray) {
	for(int i=0; i<mySimpleArray.length; i++) {
		sout(mySimpleArray[i]+"\t");
	}
}

Nested loops can also inclued while and do/while

int hungryHippo = 8;
while(hungryHippo>0) {
	do {
		hungryHippo -= 2;
	} while(hungryHippo>5);
	hungryHippo--;
	sout(hungryHippo + ", ");
}

Optional labels

if, switch and loops can all have optional labels. A label is an optional pointer to the head of a statement that allows the flow to jump to it or break from it.

Taking the previous example

int[][] myComplexArray = { {5,2,1,3}, {3,9,8,9}, {5,7,12,700} };

OUTER_LOOP: for(int[] mySimpleArray : myComplexArray) {
	INNER_LOOP: for(int i=0; i<mySimpleArray.length; i++) {
		sout(mySimpleArray[i]+"\t");
	}
}

Labels follow the same rules for formatting as identifiers. They’re commonly expressed using uppercase lettters, with underscores between words. They’re extremely useful in nested structures.

(!) The following is out of exams’ scope(!) It’s also possible to add optional labels to control and block statements

int frog = 15;
BAD_IDEA: if(frog>10)
EVEN_WORSE_IDEA: {
	frog++;
}

The break statement

A break statement transfers the flow of control out of the enclosing statement. The same holds true for a while, do/while or for loop.

Without a label parameter, the break statement will terminate the nearest inner loop it’s currently in the process of executing. The optional label parameter allows us to break out of a higher level outer loop.

PARENT_LOOP: for(int i=0; i<list.length; i++) {
	for(int j=0; j<list[i].length; j++) {
		if(list[i][j]==searchValue) {
			positionX = i;
			positionY = j;
			break PARENT_LOOP;
		}
	}
}

The previous break PARENT_LOOP; statement will break out of the entire loop structure. The alternative without a label

PARENT_LOOP: for(int i=0; i<list.length; i++) {
	for(int j=0; j<list[i].length; j++) {
		if(list[i][j]==searchValue) {
			positionX = i;
			positionY = j;
			break;
		}
	}
}

Would break from the inner loop.

The continue statement

The syntax of the continue statement mirrors that of the break. They’re identical in how they’re used, but with different results.

The continue statement ends the current iteration of the loop. It’s applied to the nearest inner loop under execution, with optional label statement to override this behaviour

PARENT_LOOP: for(int i=0; i<list.length; i++) {
	for(int j=0; j<list[i].length; j++) {
		if(list[i][j]==searchValue) {
			positionX = i;
			positionY = j;
			continue;
		}
	}
}

The return statement

Using return statements can be used as alternative to using labels and break statements. Check this revised example

PARENT_LOOP: for(int i=0; i<list.length; i++) {
	for(int j=0; j<list[i].length; j++) {
		if(list[i][j]==searchValue) {
			return new int[] {i, j};
		}
	}
}

Unreachable Code

Any code places after break, continue and return is that any code placed immediately after them in the same block, is considered unreachable and will not compile.

int checkDate = 0;
while(checkDate<10) {
	checkDate++;
	if(checkDate>100) {
		break;
		checkDate++; // DOES NOT COMPILE. UNREACHABLE.
	}
}

It doesn’t matter if the structure actually visits the line of coed. It won’t compile.

Exam Essentials

Understand if and else decision control statements. The if and else statements come up frequently throughout the exam in questions unrelated to decision control, so make sure you fully understand these basic building blocks of Java.

Understand switch statements and their proper usage. You should be able to spot a poorly formed switch statement on the exam. The switch value and data type should be compatible with the case statements, and the values for the case statements must evaluate to compile-time constants. Finally, at runtime a switch statement branches to the first matching case , or default if there is no match, or exits entirely if there is no match and no default branch. The process then continues into any proceeding case or default statements until a break or return statement is reached.

Understand while loops. Know the syntactical structure of all while and do / while loops. In particular, know when to use one versus the other.

Be able to use for loops. You should be familiar with for and for-each loops and know how to write and evaluate them. Each loop has its own special properties and structures. You should know how to use for-each loops to iterate over lists and arrays.

Understand how break , continue , and return can change flow control. Know how to change the flow control within a statement by applying a break , continue , or return statement. Also know which control statements can accept break statements and which can accept continue statements. Finally, you should understand how these statements work inside embedded loops or switch statements.