Controlling the Flow of Java Programs
- An expression is a code fragment consisting of variables, constants, method invocations, and operators that evaluates to a single value.
Examples:
-
2 + 2
-
a = b + c
-
myMethod()
-
- A statement is a complete unit of execution.
Many types of expressions can be made into statements by terminating them with a semicolon (
;
).-
Assignment expressions:
answer = 42;
-
Increment or decrement expressions:
a++;
orb--;
-
Method invocations:
System.out.println("Hello, world!");
-
Object creation:
Account client = new Account();
-
Assignment expressions:
-
A declaration statement declares a variable, optionally assigning it an initial value. For example:
int count;
A block is a group of zero or more statements between balanced braces (
{ }
).- A block can be used anywhere a single statement is allowed.
- A control flow statement is a structure controlling the execution of other statements. We’ll explore these through the rest of this module.
Java whitespace characters are the space, tab, carriage return, newline (aka linefeed), and formfeed characters.
Java requires one or more whitespace characters between keywords and/or identifiers. Other whitespace characters that do not occur within quoted string literals are not considered significant and serve only to increase the readability of code.
All variables declared within a block are known as local variables, which are stored in a memory location known as the stack. Examples of where blocks are used include:
- Method definitions
- Flow control statements
- Other blocks contained within a sequence of statements
The stack grows and shrinks as the program runs.
- Entering a block expands the stack, storing local variables in memory.
- Leaving a block shrinks the stack, flushing local variables from memory.
The scope of a local variable — that is, the context in which is visible and accessible to your program — is limited to the block in which it is declared, and all nested blocks.
Note Java does not allow you to declare a local variable with the same name as a local variable already in the same scope.
public static void main(String[] args) { int x = 1; // A local scope where args and x are visible while (x <= 5) { String msg = "x is now " + x; // A local scope where args, x, and msg are visible System.out.println(msg); } // msg is no longer in scope, and has been flushed from memory }
As previously mentioned, local variables must be initialized before they are used.
A local variable is visible only in the block of code (or sub blocks) where it is declared. Along with the variable’s value, its name is also flushed as the stack shrinks, allowing the same variable to be re-declared in another block of code.
![]() | Note |
---|---|
Method parameters are also considered local variables, because they too are declared (and initialized automatically by the calling code) on the stack. |
![]() | Tip |
---|---|
It is a good practice to limit the scope of local variables as much as possible. This promotes variable name reuse. In the case of objects, limiting local variable scope allows the JVM to quickly reclaim the memory occupied by objects that are no longer in use. |
-
The
return
statement breaks out of the entire method. - It must return a value of the type specified in the method’s signature.
-
It must not return a value if the method’s return value is of type
void
. The value can be an expression:
public static int max(int x, int y) { return (x > y) ? x : y; }
public class FindNumber { public static void main(String[] args) { if (args.length != 1) { System.err.println("Usage: FindNumber <num>"); return; } int[] numbers = {1, 3, 4, 5, 7, 5, 2, 8, 9, 6}; int findNumber = Integer.parseInt(args[0]); System.out.println(find(numbers, findNumber)); } public static int find(int[] nums, int num) { for (int i = 0; i < nums.length; i++) { if (nums[i] == num) { return i; } } return -1; } }
The
if-else
statement defines a block of code that is to be executed based on some boolean condition.if (boolean-expression) { // Run if BE is true } else if (boolean-expression2) { // Run if BE is false and BE2 is true } else { // Run if both BE and BE2 are false }
-
The
else if
part is optional and can appear many times between theif
andelse
parts. -
The
else
part is optional but can appear only once afterif
and all/anyelse if
parts.
public class LetterGrade { public static void main(String[] args) { if (args.length != 1) { System.out.println("Usage: LetterGrade <numeric-score>"); return; } int score = Integer.parseInt(args[0]); char grade; if (score >= 90) { grade = 'A'; } else if (score >= 80) { grade = 'B'; } else if (score >= 70) { grade = 'C'; } else if (score >= 60) { grade = 'D'; } else { grade = 'F'; } System.out.println("Grade = " + grade); } }
![]() | Note |
---|---|
In this example, the numerical score is parsed from a string (the first and only command-line argument) into an integer data type. |
The
switch
statement is a special-purposeif-else-if-else
construct that is easier and shorter to write:switch(expression) { case const1: /* do X */ break; case const2: /* do Y */ break; default: /* do something else */ }
-
The
switch
expression must be an integral type other thanlong
:byte
,short
,char
, orint
. -
case
values must be constants (not variables). -
case
values are tested for equality only (==
).
-
The
public class MonthFromNumber { public static void main(String[] args) { if (args.length != 1) { System.err.println("Usage: MonthFromNumber <month>"); return; } int month = Integer.parseInt(args[0]); switch (month) { case 1: System.out.println("January"); break; case 2: System.out.println("February"); break; case 3: System.out.println("March"); break; case 4: System.out.println("April"); break; case 5: System.out.println("May"); break; case 6: System.out.println("June"); break; case 7: System.out.println("July"); break; case 8: System.out.println("August"); break; case 9: System.out.println("September"); break; case 10: System.out.println("October"); break; case 11: System.out.println("November"); break; case 12: System.out.println("December"); break; default: System.out.println("Invalid month: " + month); } } }
![]() | Note |
---|---|
You can also switch on enumerated values defined by |
A
case
is an entry point into aswitch
statement, whereas abreak
acts as an exit point.-
Typically each
case
has a trailingbreak
. Without a
break
, execution automatically “falls through” to the statements in the followingcase
.Tip If you intentionally fall through to the following
case
, include a comment to alert maintenance programmers of your intent. Otherwise, they might think you simply forgot thebreak
and add it in later.
-
Typically each
-
The optional
default
case cannotbreak
and is executed if no othercase
s match andbreak
prior to it. -
To exit both the
switch
statement as well as the method in which it is defined, use areturn
in place of abreak
.
public class DaysInMonth { public static void main(String[] args) { if (args.length != 2) { System.err.println("Usage: DaysInMonth <year> <month>"); return; } int year = Integer.parseInt(args[0]); int month = Integer.parseInt(args[1]); int numDays = 0; switch (month) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: numDays = 31; break; case 4: case 6: case 9: case 11: numDays = 30; break; case 2: numDays = ( ( (year % 4 == 0) && !(year % 100 == 0)) || (year % 400 == 0) )? 29 : 28; break; default: System.err.println("Invalid month " + month); return; } System.out.println("Number of Days = " + numDays); } }
The
while
loop statement executes a block of statements as long as a condition remains true:while (boolean-expression) { // Do something repeatedly // Update condition }
To ensure the loop body runs at least once, a
do-while
variant executes the statement block before evaluating the condition:do { // Do something at least once } while (boolean-expression);
![]() | Note |
---|---|
Remember to update the condition to avoid infinite loops. |
public class WhileArguments { public static void main (String[] args) { int i = 0; while (i < args.length) { System.out.println(args[i]); i += 1; } } }
The
for
loop statement is similar to thewhile
loop, but allows compact initialization of an iterator variable and its increment/decrement:for (init; condition; update) { // Do something }
- The initialization statement runs before anything else.
- The condition is evaluated before each repetition of the loop body.
-
The update statement is run after each body repetition. This can consist of multiple expression statements separated by commas (
,
).
![]() | Note |
---|---|
You can declare a local variable in the for (int i = 1; i <= 10; i++) { // i is local to this scope } |
public class ForArguments { public static void main(String[] args) { for (int i = 0; i < args.length; i++) { System.out.println(args[i]); } } }
![]() | Tip |
---|---|
You can omit the initialization and/or update statements from the // x has been declared and initialized elsewhere for (; x < 5; x++) System.out.println(x); |
Java 5 also introduced an advanced
for
syntax known asfor-each
.- It is designed specifically for iterating over collections of data, whether whose collections are arrays, or some predefined collection classes (discussed in the Java Collections Framework module).
The
for-each
statement has the following syntax:for (Type element: collectionOfType) { // Do something with element }
For example:
public class ForEachArguments { public static void main(String[] args) { for (String arg: args) { System.out.println(arg); } } }
In addition to defining the exit points from cases in a
switch
statement, thebreak
statement stops the execution of a loop:while (someCondition) { // Do something if (someOtherCondition) { break; } // Do something else }
public class SearchForNumber { public static void main (String[] args) { int[] nums = {1, 5, 4, 43, -2, 6, 4, 9 }; int search = 4; for (int i = 0; i < nums.length; i++) { if (nums[i] == search) { System.out.println("Found " + search + " at position " + i); break; } } } }
- Skips one execution of a loop’s body
With
continue
, this:while (someCondition) { if (someOtherCondition) { continue; } // Do something }
works the same as this:
while (someCondition) { if (!someOtherCondition) { // Do something } }
public class SkipOddNumbers { public static void main(String[] args) { int[] nums = { 0, 3, 4, 5, 7, 2, 6, 9, 8, 7, 1 }; for (int i = 0; i < nums.length; i++) { if (nums[i] % 2 != 0) { continue; } System.out.println(nums[i] + " is even"); } } }
Looping structures can be nested.
-
By default, a
break
orcontinue
statement affects the innermost loop in which it is located.
-
By default, a
You can apply an optional label to a looping structure.
-
The label is an identifier followed by a
:
placed before the looping structure.
-
The label is an identifier followed by a
Both the
break
and thecontinue
statement accept an optional label argument.-
In that case, the
break
orcontinue
statement affects the looping structure with that label.
-
In that case, the
OUTER: for (int i = 1; i <= 10; i++) { for (int j = 1; j <= 10; j++) { if (i == j) continue OUTER; } }
public class SearchForNumber2D { public static void main (String[] args) { int[][] nums = { {1, 3, 7, 5}, {5, 8, 4, 6}, {7, 4, 2, 9} }; int search = 4; foundNumber: for (int i = 0; i < nums.length; i++) { for (int j = 0; j < nums[i].length; j++) { if (nums[i][j] == search) { System.out.println( "Found " + search + " at position " + i + "," + j); break foundNumber; } } } } }