What do for(;;);, while(true);, and do;while(true); have in common? Each of these loop statements presents an extreme example of an infinite loop (a loop that never ends).
An infinite loop is something that you should avoid because its unending execution causes your application to hang, which is not desirable from the point of view of your application’s users.
CAUTION: An infinite loop can also arise from a loop header’s Boolean expression comparing a floating-point value against a nonzero value via the equality or inequality operator, because many floating-point values have inexact internal representations. For example, the following code fragment never ends because 0.1 does not have an exact internal representation:
for (double d = 0.0; d != 1.0; d += 0.1) System.out.println(d);
However, there are times when it is handy to code a loop as if it were infinite by using one of the aforementioned programming idioms. For example, you might code a
while(true) loop that repeatedly prompts for a specific keystroke until the correct key is pressed.
When the correct key is pressed, the loop must end. Java provides the break statement for this purpose.
The break statement transfers execution to the first statement following a switch statement (as discussed earlier) or a loop. In either scenario, this statement consists of reserved word break followed by a semicolon.
Listing 2–20 uses break with an if decision statement to exit a while(true)-based infinite loop when the user presses the C or c key.
Listing 2–20. Breaking out of an infinite loop int ch; while (true) { System.out.println("Press C or c to continue."); ch = System.in.read(); if (ch == 'C' || ch == 'c') break; }
The break statement is also useful in the context of a finite loop. For example, consider a scenario where an array of values is searched for a specific value, and you want to exit the loop when this value is found. Listing 2–21 reveals this scenario.
Listing 2–21. Prematurely breaking out of a for-based loop int[] employeeIDs = { 123, 854, 567, 912, 224 }; int employeeSearchID = 912;
boolean found = false;
for (int i = 0; i < employeeIDs.length; i++) if (employeeSearchID == employeeIDs[i]) {
found = true; break; }
System.out.println((found) ? "employee " + employeeSearchID + " exists" : "no employee ID matches " + employeeSearchID);
Listing 2–21 uses for and if to search an array of employee IDs to determine if a specific employee ID exists. If this ID is found, if’s compound statement assigns true to found. Because there is no point in continuing the search, it then uses break to quit the loop. The continue statement skips the remainder of the current loop iteration, reevaluates the header’s Boolean expression, and performs another iteration (if true) or terminates the loop (if false). Continue consists of reserved word continue followed by a semicolon. Consider a while loop that reads lines from a source and processes nonblank lines in some manner. Because it should not process blank lines, while skips the current iteration when a blank line is detected, as demonstrated in Listing 2–22.
Listing 2–22. Skipping the remainder of the current iteration String line;
while ((line = readLine()) != null) {
if (isBlank(line)) continue; processLine(line); }
Listing 2–22 employs a fictitious isBlank() method to determine if the currently read line is blank. If this method returns true, if executes the continue statement to skip the rest of the current iteration and read the next line whenever a blank line is detected.
Look carefully at Listing 2–22 and you should realize that the continue statement is not needed. Instead, this listing can be shortened via refactoring (rewriting source code to improve its readability, organization, or reusability), as demonstrated in Listing 2–23. Listing 2–23. A refactored if statement
String line;
while ((line = readLine()) != null) {
if (!isBlank(line)) processLine(line); }
Listing 2–23’s refactoring modifies if’s Boolean expression to use the logical
complement operator (!). Whenever isBlank() returns false, this operator flips this value to true and if executes processLine().
Unlike break, continue does not appear to be a necessary part of the language. As you have just seen, it is possible to remove continue by inverting the Boolean expression. If you find yourself relying too much on continue, perhaps you need to refactor your code. Java provides labeled versions of break and continue as disciplined versions of goto, a statement that transfers execution to a labeled statement. (Many developers hate goto because undisciplined use of this statement results in unreadable/unmaintainable code.)
NOTE: Java reserves the goto identifier so that it cannot be used to name any source code entity. However, GOTO is not reserved, so it seems pointless to reserve goto.
The labeled break statement consists of break, followed by an identifier for which a matching label (an identifier followed by a colon) must exist. Furthermore, the label must immediately precede a loop statement.
Labeled break is useful for breaking out of nested loops (loops within loops). For example, Listing 2–24 reveals the labeled break statement transferring execution to the first statement that follows the outer for loop.
Listing 2–24. Breaking out of nested for loops outer:
for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) if (i == 1 && j == 1) break outer; else
System.out.println("i=" + i + ", j=" + j); System.out.println("Both loops terminated.");
When i’s value is 1 and j’s value is 1, break outer; is executed to terminate both for loops. This statement transfers execution to the first statement after the outer for loop, which happens to be System.out.println("Both loops terminated.");.
The following output is generated: i=0, j=0
i=0, j=1 i=0, j=2 i=1, j=0
Both loops terminated.
The labeled continue statement consists of continue, followed by an identifier for which a matching label (an identifier followed by a colon) must exist. Furthermore, the label must immediately precede a loop statement.
Labeled continue is useful for terminating the current and future iterations of nested loops and beginning a new iteration of the labeled loop. For example, Listing 2–25 reveals the labeled continue statement terminating the inner for loop’s iterations.
Listing 2–25. Continuing the outer for loop outer:
for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) if (i == 1 && j == 1) continue outer; else
System.out.println("i=" + i + ", j=" + j); System.out.println("Both loops terminated.");
When i’s value is 1 and j’s value is 1, continue outer; is executed to terminate the inner for loop and continue with the outer for loop at its next value of i. Both loops continue until they finish.
The following output is generated: i=0, j=0 i=0, j=1 i=0, j=2 i=1, j=0 i=2, j=0 i=2, j=1 i=2, j=2
Both loops terminated.