Control Structures in Java

Control structures are structures which alter the regular sequential flow of a program. In this tutorial we are going to have a look at the arguably most important ways of using the control structures of the Java programming language.

Control structures are structures which alter the regular sequential flow of a program. We differentiate between

  • Conditional statements (if-then, if-then-else, switch) which allow the execution of different code branches depending on some conditions;
  • Looping statements or iteration statements (while, do-while, for, foreach) which enable the repetition of code sequences for a certain amount of times or for certain objects (especially in the case of the foreach statement);
  • Branching statements are statements which enable changes in the normal flow of a loop or switch statement (in case of the break statement). We will deal in this tutorial with the labeled and unalabeled break and continue statements.

Let’s have a look at the arguably most important ways of using the control structures in the Java programming language.

Conditional Statements

The if Statement

The if statement makes the conditional execution of code possible (that is why it is also called, together with the switch statement, a selection statement). In its simplest form an if statement looks like this (if-then statement):

if (condition) {
	System.out.println("condition is true");
}

If the expression which represents the condition evaluates to true, the code block inside the if statement is executed, otherwise not.

The if-then-else statement enables the execution of either of two code branches depending on the state of a condition. An example:

if (condition) {
	System.out.println("condition is true");
} else {
	System.out.println("condition is false");
}

The ternary operator (or conditional operator) can be viewed as a simplification of the if-then-else statement. The main difference is that the conditional operator is an expression while the if statement is, as the name suggests, a statement. This means the ternary operator produces a result while the if statement is simply executed. Not every if-then or if-then-else statement can be replaced by a ternary operator, but there are cases when the ternary operator comes in handy. Let’s see an example:

String ret = condition ? "TRUE" : "FALSE";

If the condition is true the ternary operator evaluates to TRUE which is assigned to ret. Analogous with the false branch. With the help of an if statement the above example would look something like this:

String ret = "";
if (condition) {
	ret = "TRUE";
} else {
	ret = "FALSE";
}

Should there be more than two possible branches to be executed the following is also possible:

enum Value { ONE, TWO, THREE };

Value val = Value.ONE;
if (val == Value.ONE) {
	System.out.println("val == ONE");
} else if (val == Value.TWO) {
	System.out.println("val == TWO");
} else {
	System.out.println("val == THREE");
}

The switch statement, which we will have a look at in a moment, might be a better alternative to express the above case.

if statements can also be nested. This might be detrimental to the readability though. While the following is allowed, it might be better to avoid it, if possible:

if (conditionOne) {
	if (conditionTwo) {
		if (conditionThree) {
			// do something
		}
	}
}

Parantheses are optional as long as only one statement has to be executed. So removing the brackets from the above example is legal:

if (val == Value.ONE)
	System.out.println("val == ONE");
else if (val == Value.TWO)
	System.out.println("val == TWO");
else
	System.out.println("val == THREE");

But again, it might be detrimental to the readability.

The switch Statement

The switch statement is a more elegant way to deal with multiple execution branches. For example:

Value value = Value.TWO;
switch (value) {
case ONE:
	System.out.println("value == ONE");
	break;
case TWO:
	System.out.println("value == TWO");
	break;
case THREE:
	System.out.println("value == THREE");
	break;
default:
	System.out.println("If value can only be ONE, TWO or THREE, this should not be possible.");
	break;
}

The default case specifies what should happen in “all other cases”. In the previous example this is of course irrelevant, since all three possible cases are already dealt with (we could also leave it out). Notice the break statement at the end of each case. If a break is missing at the end of one execution branch the next case will also be executed. This is called fall through. Let us have a look at a small example:

Value value = Value.TWO;
switch (value) {
case ONE:
	System.out.println("value == ONE");
case TWO:
	System.out.println("value == TWO");
case THREE:
	System.out.println("value == THREE");
}

The result is:

value == TWO
value == THREE

Starting with Java SE 14 the usage of a switch “statement” as an expression is a permanent feature. So the following code is legal:

Value value = Value.TWO;
int intVal = switch (value) {
case ONE -> 1;
case TWO -> 2;
case THREE -> 3;
};
System.out.println("Value of intVal: " + intVal);

and leads to the following output:

Value of intVal: 2

With the yield statement (available also since Java SE 14 as a permanent feature) the above example could also look like this:

Value value = Value.TWO;

int intVal = switch (value) {
case ONE:
	yield 1;
case TWO:
	yield 2;
case THREE:
	yield 3;
};

System.out.println("Value of intVal: " + intVal);

which leads of course to the same output:

Value of intVal: 2

The switch statement (and expression) is meanwhile a rather complex feature with various other capabilities and restrictions, which will not be explored further in this article.

Let us move to the next topic, that of the looping statements in Java.

Looping Statements

The for Statement

The for statement comes in two flavors: basic and enhanced (aka foreach). In its basic version the for statement looks like this:

for([Initialization Code]; [Condition]; [Update Code]) {
	// do something
}

All three parts (initialization code, condition and update code) are optional, so this is legal:

for(;;); // infinite loop

It’s an infinite loop though, as are the following examples:

for(int i = 0;;); // still an infinite loop
for (int i = 0; i <= 4;); // still an infinite loop
for (;; System.out.println("ad infinitum")); // still an infinite loop

Infinite loops make sometimes sense as we will see in a moment, but generally they are a bug.

Here is an example of how you normally would want to use a basic for statement:

for (int i = 1; i <= 5; i++) {
	System.out.println("Iteration number: " + i);
}

This repeats the code inside the for loop five times (the code is executed as long as the condition is true):

Iteration number: 1
Iteration number: 2
Iteration number: 3
Iteration number: 4
Iteration number: 5

The enhanced for loop iterates over an array or collection (or more precise: a subtype of the raw type Iterable). It looks like this:

for(Type variable : [some iterable or array]) {
	// do something
}

Here is an example with an array:

int[] values = { 1, 2, 3, 4, 5 };
for (int val : values) {
	System.out.println("Value is: " + val);
}

It prints:

Value is: 1
Value is: 2
Value is: 3
Value is: 4
Value is: 5

This works analogous with a list:

List<Integer> values = List.of(1, 2, 3, 4, 5);
for (Integer val : values) {
	System.out.println("Value is: " + val);
}

As expected, the output is the same as above.

The while and do-while Statements

The while statement repeats a code block as long as a condition is true. An example: in order to repeat a code block five times you could write:

int j = 0;
while (j < 5) {
	System.out.println(j++);
}

Since the while loop first checks whether the condition is true, the containing code will not be executed if the condition is false.

Should the code be executed at least once no matter what, the do-while statement is the appropriate construct. The following code:

int i = 0;
do {
	System.out.println("I'm entering here.");
} while (i < 0);

// i == 0
while (i < 0) {
	System.out.println("But not here.");
}

prints as expected:

I'm entering here.

Infinite Loops

Infinite loops can be a bug or a feature. An example for an infinite loop which is a feature would be a controller which regularly polls a sensor for new values.

We have seen already an infinite loop with a for statement:

for (;;) {
	// poll sensor
}

You can achieve the same outcome with a while or do-while loop:

while (true) {
	// poll sensor
}

do {
	// poll sensor
} while (true);

This concludes the looping statements and brings us to the last topic of this tutorial: the branching statements.

Branching Statements

The branching statements break and continue can be labeled or unlabeled. Labeled break and continue statements are reminiscent of the goto keyword in other languages. Java does not support goto, but goto is nonetheless a reserved keyword which currently (as of Java SE 17) is not used. From my experience labeled break and continue are seldom used features, at least I don’t remember seeing them ever in production code. Nonetheless since they are part of the Java language I think one should be aware of them.

The break Statement

We have already seen the break statement in conjunction with the switch statement. The break statement can also come in handy if you want to break out of a loop prematurely (for example because some condition is already met and iterating further is not necessary). An example:

for (int i = 0; i < 100; i++) {
	System.out.println("Iteration number: " + i);
	if (i == 1) {
		break;
	}
}

This iterates twice and breaks afterwards out of the loop:

Iteration number: 0
Iteration number: 1

Another example:

while (true) {
	break; // not really an infinite loop
}

The break finishes in this example the (otherwise infinite) while loop.

And here is an example with a labeled break:

label: while (true) {
	System.out.println("outer loop");
	while (true) {
		System.out.println("inner loop");
		break label; // this gets us out completely
	}
}
System.out.println("out of the loops");

An unlabeled break would only get us out of the inner loop, we would still have an infinite loop in the above example. With a labeled break we can also directly break out of the outer loop. And the output is:

outer loop
inner loop
out of the loops

The continue Statement

The continue statement is allowed in conjunction with loop statements (while, do-while, for). Let us have a look at an unlabeled continue statement:

int i = 0;
while(i++ <= 3) {
	if(i == 2) {
		continue; // We won't see 'Value of i: 2'
	}
	System.out.println("Value of i: " + i);
}

The output of the above code is:

Value of i: 1
Value of i: 3
Value of i: 4

So if i is 2 the loop will break the current iteration and continue with the next iteration (without executing the println statement after the if statement).

And now let us have a look at a labeled continue statement:

int i = 0;
label: while (true) {
	System.out.println("outer loop");
	while (++i <= 3) {
		System.out.println("inner loop | i == " + i);
		if (i == 2) {
			continue label;
		}
	}
	System.out.println("break out of the loop");
	break;
}

The first loop is an infinite one. After entering it we of course expect it to print „outer loop“. The variable i is 0 and is incremented inside the second while loop. The second while should execute twice before i equals 2 and control is transferred outside it. We are again in the outside loop with i == 2. This means one more round to go for the inner loop before we continue with the break statement which gets us completely out of the (infinite) outer while loop.

As expected the output is:

outer loop
inner loop | i == 1
inner loop | i == 2
outer loop
inner loop | i == 3
break out of the loop

Conclusion

This short tutorial aims to provide an overview of arguably the most important ways of using the control structures in the programming language Java.

Depending on the language version used, some of the features presented in this tutorial might not be available, new features might (or rather will surely) be added in the future. Various subtleties, restrictions or additional capabilities exist for the presented control structures. A complete account of the supported features is of course provided by the Java Language Specification (I used the 17th version as a reference for this tutorial).

I hope you found the tutorial helpful. I wish you happy coding!


You might also like the following articles and book reviews:

Petre Sora

Petre Soras Interessen sind vielfältig und befinden sich an der Schnittstelle zwischen Mensch und Informationstechnologie. Nachdem er sein Bachelorstudium im Fach Psychologie abschloss, orientierte er sich neu und studierte Software Engineering an der Hochschule Heilbronn, ebenfalls mit einem Bachelor abgeschlossen. Anschließend war er knappe sechs Jahre als Java-Entwickler in mehreren Unternehmen tätig. Aktuell ist er mit dem Masterstudium der Praktischen Informatik und der Rezensionsplattform für IT-Fachbücher nososo.de beschäftigt.

Schreibe einen Kommentar