Control Flow
Control Flow
Control flow is how a program decides what to do next. There are only two ideas: choose between alternatives, and repeat something. Everything else is a variation.
Java's syntax here is inherited from C, so if you have written C or C++ this will feel familiar. Two differences are worth flagging up front. Conditions must be genuinely boolean, not just non-zero, and Java has an enhanced for loop for collections.
Branching with if
public class Main {
public static void main(String[] args) {
int marks = 63;
if (marks >= 90) {
System.out.println("Grade: A");
} else if (marks >= 75) {
System.out.println("Grade: B");
} else if (marks >= 60) {
System.out.println("Grade: C");
} else if (marks >= 40) {
System.out.println("Grade: D");
} else {
System.out.println("Grade: F");
}
boolean attendanceOk = true;
if (marks >= 40 && attendanceOk) {
System.out.println("Result: Pass");
} else {
System.out.println("Result: Not cleared");
}
String status = (marks >= 40) ? "Cleared" : "Backlog";
System.out.println("Status: " + status);
}
}
The chain is checked top to bottom and stops at the first true test. Order matters: if marks >= 40 came first, a student with 95 would be graded D.
The comparison operators are ==, !=, <, >, <=, >=. The logical operators are &&, || and !. Both && and || short-circuit: in a && b, if a is false, b is never evaluated. This lets you put a safety check on the left, as in if (s != null && s.length() > 0), where evaluating the right side first would throw.
The last line uses the ternary operator, condition ? valueIfTrue : valueIfFalse. It is an expression, so it produces a value you can assign. Good for one small choice, bad for anything longer.
In C, if (x = 5) compiles: it assigns 5 and treats the non-zero result as true. In Java that same line does not compile, because x = 5 produces an int and if demands a boolean. Whole categories of typo simply cannot happen. It does still compile for boolean variables, so if (flag = true) slips through. Write if (flag).
switch
When you are testing one value against a list of exact possibilities, switch reads better than a long chain.
public class Main {
public static void main(String[] args) {
int day = 3;
switch (day) {
case 1:
System.out.println("Monday");
break;
case 2:
System.out.println("Tuesday");
break;
case 3:
System.out.println("Wednesday");
break;
case 6:
case 7:
System.out.println("Weekend");
break;
default:
System.out.println("Not a valid day");
break;
}
String subject = "Maths";
switch (subject) {
case "Maths":
System.out.println("Credits: 4");
break;
case "Physics":
System.out.println("Credits: 3");
break;
default:
System.out.println("Unknown subject");
break;
}
char grade = 'B';
switch (grade) {
case 'A': System.out.println("Excellent"); break;
case 'B': System.out.println("Good"); break;
case 'C': System.out.println("Average"); break;
default: System.out.println("Needs work"); break;
}
}
}
Without break, execution falls through into the next case and keeps going. Stacking case 6: above case 7: with no statements between them uses this deliberately so both share one body. Forgetting break elsewhere is a genuine bug that the compiler will not catch.
Unlike C and C++, Java can switch on a String. It can also switch on int, char, byte, short and enums, but not on double or boolean.
Counted repetition with for
public class Main {
public static void main(String[] args) {
for (int i = 1; i <= 5; i++) {
System.out.println("Pass " + i);
}
int sum = 0;
for (int i = 1; i <= 10; i++) {
sum += i;
}
System.out.println("Sum 1 to 10 = " + sum);
for (int i = 10; i >= 1; i -= 2) {
System.out.print(i + " ");
}
System.out.println();
int n = 5;
for (int i = 1; i <= 10; i++) {
System.out.printf("%d x %2d = %3d%n", n, i, n * i);
}
}
}
The header has three parts separated by semicolons: initialisation, a condition tested before each pass, and an update run after each pass. Declaring int i inside the header keeps it local to the loop, which is what you want.
while and do-while
while is for when the number of passes is not known in advance.
public class Main {
public static void main(String[] args) {
int n = 9875;
int copy = n;
int digits = 0;
while (copy > 0) {
copy /= 10;
digits++;
}
System.out.println(n + " has " + digits + " digits");
copy = n;
int reversed = 0;
while (copy > 0) {
reversed = reversed * 10 + (copy % 10);
copy /= 10;
}
System.out.println("Reversed: " + reversed);
int countdown = 3;
do {
System.out.println("Tick " + countdown);
countdown--;
} while (countdown > 0);
int total = 0;
for (int i = 1; i <= 20; i++) {
if (i % 3 == 0) continue;
if (i > 15) break;
total += i;
}
System.out.println("Filtered total: " + total);
}
}
do-while tests after the body, so the body always runs at least once. That is the right shape for a menu or for retrying invalid input, where you have to ask before you can check the answer. Note the semicolon after the closing while (...); it is required here and nowhere else.
continue skips the rest of the current pass. break leaves the loop entirely. Both affect only the innermost loop.
The enhanced for loop
When you want to visit every element of an array or collection and do not need the index, the enhanced for says exactly that.
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
int[] marks = {78, 65, 92, 41, 88};
int total = 0;
for (int m : marks) {
total += m;
}
System.out.println("Total: " + total);
System.out.printf("Average: %.2f%n", (double) total / marks.length);
for (int m : marks) {
if (m < 50) {
System.out.println(m + " is below the pass mark");
}
}
ArrayList<String> subjects = new ArrayList<>();
subjects.add("Maths");
subjects.add("Physics");
subjects.add("Chemistry");
for (String s : subjects) {
System.out.println("- " + s);
}
String word = "compiler";
int vowels = 0;
for (char c : word.toCharArray()) {
if ("aeiou".indexOf(c) >= 0) vowels++;
}
System.out.println(word + " has " + vowels + " vowels");
}
}
Read for (int m : marks) as "for each int m in marks". There is no index, so there is no off-by-one error possible and no risk of running past the end.
The limitation is real, though: m is a copy of the element, so assigning to it does not change the array. When you need to modify elements or need the position, use an indexed for.
Common mistakes
- A stray semicolon after a loop header.
for (int i = 0; i < 5; i++);runs an empty body five times, then your block once. Perfectly legal, silently wrong. - Forgetting
breakin aswitch. Execution falls into the following cases. - A condition that never becomes false.
while (n != 0) n -= 2;never ends for oddn. Prefern > 0. - Comparing strings with
==in a condition. Use.equals();==compares references. - Assigning to the loop variable of an enhanced
forand expecting the array to change. It is a copy. Use an indexed loop. - Using
i <= array.length. Valid indices stop atlength - 1, so this throwsArrayIndexOutOfBoundsException. Usei < array.length.
Branching and looping turn a list of statements into a program that solves something. Write a multiplication table, a prime check and a digit-reversal program before moving on, or find them ready on the Practice page.