2: Flow ControlS2C Home « 2: Flow Control

In this lesson we look at the different syntax we can use to affect the flow of logic through our programs. We start by looking at code to implement if and switch statements whilst identifying legal argument types for them. We then investigate the different types of loops available in java and the iterators and their usage within a loop. We will also look at how we can break from a loop, continue from within a loop and label a loop to continue execution from. We continue our study of flow control by looking at assertions and when, and when not, they should be used. We finish our certification lessons on flow control by looking at exceptions, handling exceptions and how to declare methods and overriding methods that throw exceptions. We also need to understand what happens to our code when an error or any type of exception occurs in a code fragment and what happens when particular exceptions occur.

Lets take a look at the points outlined at the Oracle Website for this part of the certification.

  • Section 2: Flow Control

    1. Develop code that implements an if or switch statement; and identify legal argument types for these statements.

    2. Develop code that implements all forms of loops and iterators, including the use of for, the enhanced for loop (for-each), do, while, labels, break, and continue; and explain the values taken by loop counter variables during and after loop execution.

    3. Develop code that makes use of assertions, and distinguish appropriate from inappropriate uses of assertions.

    4. Develop code that makes use of exceptions and exception handling clauses (try, catch, finally), and declares methods and overriding methods that throw exceptions.

    5. Recognize the effect of an exception arising at a specified point in a code fragment. Note that the exception may be a runtime exception, a checked exception, or an error.

    6. Recognize situations that will result in any of the following being thrown: ArrayIndexOutOfBoundsException, ClassCastException, IllegalArgumentException, IllegalStateException, NullPointerException, NumberFormatException, AssertionError, ExceptionInInitializerError, StackOverflowError or NoClassDefFoundError. Understand which of these are thrown by the virtual machine and recognize situations in which others should be thrown programmatically.

The if StatementTop

Create a conditional expression using one of the relational operators available in Java to test one operand against another or test the result of a logical operation. We can execute one set of statements if the boolean result of the expression evaluates to true and another if the expression evaluates to false.

The relational and logical operators are discussed in more detail in the Operators lesson.

The following table shows the different forms of the if construct that can be used. We are using blocks of code to wrap statements which is optional, but good practice and will be used here.

Construct Description
Simple if
if (condition) {
    statement1
}

Execute statements in statement1  if condition expression evaluates to true.
if....else
if (condition) {
    statement1
} else {
    statementN
}

Execute statements in statement1  if condition expression evaluates to true,

otherwise execute statements in statement N.
Nested if
if (condition) {
    statement1
    if (condition2) {
        statement2
    } else {
        statement3
    }
} else {
    statement N
}

Execute statements in statement1  if condition expression evaluates to true

Execute statements in statement2  if condition2 expression evaluates to true,

otherwise execute statements in statement3  if condition2 expression evaluates to false,


otherwise execute statements in statement N  if condition expression evaluates to false.
Multiple if....else if
if (condition) {
    statement1

} else if (condition2) {
    statement2

} else if (condition3) {
    statement3
   ...
} else {
    statement N
}

Execute statements in statement1 if  condition expression evaluates to true


Execute statements in statement2  if condition2 expression evaluates to true,


Execute statements in statement3  if condition3 expression evaluates to true etc...,


otherwise execute statements in statement N.

if Rules and Examples

There are a few rules to remember when coding the if statement:

  • The expression we check against, in the parentheses, must evaluate to a boolean type: true or false.
  • One optional else statement can be coded as part of the if construct and must come after any else if statements.
  • Multiple else if statements can be coded after the initial if and before the optional else statement (if coded).
  • As soon as we hit a true condition any other else if statements will not be tested.

Following are some examples showing if statement usage:


/*
  Some code showing if statement usage
*/
boolean b = false;
int i = 2;

 // Simple if
if (b == false) {
    System.out.println("This is executed");
}   

if (i = 2) { // Will not compile, does not evaluate to boolean
    System.out.println(" ");
}   
// if else
if (b = false) { // Assignment not evaluation
    System.out.println("This is NOT executed"); 
} else {
    System.out.println("This is executed");
}   
// Multiple if else
if (i == 0) { 
    System.out.println("This is NOT executed"); 
} else if (i == 1) {
    System.out.println("This is NOT executed");
} else if (i == 2) {
    System.out.println("This is executed");
} else {
    System.out.println("This is NOT executed");
}   

if (i == 0) { 
    System.out.println("This is NOT executed"); 
} else (i == 1) { // Will not compile, else must come last
    System.out.println(" ");
} else if {
    System.out.println(" ");
}

The switch StatementTop

The switch statement can be used instead of the multiple if....else if construct when we are doing a multiway test on the same value. When this scenario arises the switch statement is often a more efficent and elegant way to write our code

The switch statement is where we put the expression we are going to evaluate. Each case constant is evaluated against the switch expression and the statements within the case are processed on a match. We have the option to break from the switch after this if required. We can also code an optional default statement, which can act as a kind of catch all and is normally placed, at the end, after the case statements.

Construct Description
switch
switch (expression) {
   case constant1:
      statements1
      [break;]

   case constant2:
      statements2
      [break;]
   ...

   case constantN:
      statementsN
      [break;]

   default:
      defaultStatements
      [break;]
}
expression can be of type char, byte, short or int.
  constant1 must be a literal of a type compatible with the switch expression.
     Execute statements1 on match.
     Optional break from switch statement.

  constant2 must be a literal of a type compatible with the switch expression.
     Execute statements2 on match.
     Optional break from switch statement.

  constantN must be a literal of a type compatible with the switch expression.
     Execute statementsN on match.
     Optional break from switch statement.

   Optional default statement (catch all).
     Execute defaultStatements.
     Optional break from switch statement.
nested switch
switch (expression1) {
   case constant1:
      switch (expression2) {
         case constant1:
         statements1
         [break;]
          ...
      } // end of inner switch

      [break;]
   ...
   default:
       defaultStatements
      [break;]
}


This is perfectly legal.

switch Rules and Examples

There are a few rules to remember when coding the switch statement:

  • The switch expression we check against, in the parentheses, must evaluate to a char type, or the byte, short or int signed numeric integer types or to an enum.
  • When not using an enum in our switch statement only types that can be implicitly cast to an int are allowed.
  • The switch statement can only check for equality (==) and so the other relational operators won't work with switch.
  • The case constant must evaluate to the same type as the switch expression.
  • The case constant is a compile time constant and because it has to be resolved at compile time, any constant or final variables must have been assigned a value.
  • The value of the case constants must be different.
  • When a case constant evaluates to true the associated code block and any code blocks following are executed, unless a break statement is encountered.
  • The default statement can be placed anywhere within the switch statement and when executed, the associated code block and any code blocks following are executed, unless a break statement is encountered.

Following are some examples showing switch statement usage:


/*
  Some code showing switch statement usage
*/
final int i1 = 2;
final int i2;
byte b = 25;
String s = "java";

 // switch
switch (i1) {
    case 1:
        System.out.println("This is not executed");
    case 2:
        System.out.println("This is executed");
    case 3:
        System.out.println("This is executed");
}   

switch (i2) {  // Not a compile time constant so won't compile
    case 1:
        System.out.println(" ");
    case 2:
        System.out.println(" ");
    case 3:
        System.out.println(" ");
}   

switch (i1) {
    case 1:
        System.out.println(" ");
    case 2:
        System.out.println(" ");
    case 2:  // Same case value so won't compile
        System.out.println(" ");
}   

switch (b) {
    case 25:
        System.out.println(" ");
    case 128:  // Loss of precision so won't compile
        System.out.println(" ");
}   

switch (s) {  // Incompatible switch type of String so won't compile
    case "java2":
        System.out.println(" ");
    case "java":
        System.out.println(" ");
    case "java":
        System.out.println(" ");
}   

 // switch break
int i3 = 2;
switch (i3) {
    case 1:
        System.out.println("This is not executed");
    case 2:
        System.out.println("This is executed");
        break;
    case 3:
        System.out.println("This is not executed");
}   

 // switch default
int i4 = 2;
switch (i4) {
    case 0:
        System.out.println("This is not executed");
    case 1:
        System.out.println("This is not executed");
    default:
        System.out.println("This is executed");
}   

switch (i4) {
    case 0:
        System.out.println("This is not executed");
    default:
        System.out.println("This is executed");
    case 1:
        System.out.println("This is executed");
}

The for StatementTop

The for statement will loop through a section of code a set number of times. The for statement is very versatile and has two different variations known commonly as the for loop and the enchanced for loop.

The for loop contains three parts. In the first part we initialize a counter variable with a value, this only happens on initial entry to the for loop. The second part is a condition which tests the variable value at the start of each loop and if the condition is no longer true the for loop is exited. The final part of the for statement is an expression to be evaluated at the end of each iteration of the loop. This normally takes the form of a counter that is decremented or incremented.

The enchanced for loop was introduced in java and implements a for-each style loop that iterates through a collection of objects in a sequential order from start to finish.

The following table shows the different forms of the for construct that can be used. We are using blocks of code to wrap statements which is optional when using a single statement, but good practice and will be used here.

Construct Description
for loop
for ([initialization]; [condition]; [iteration]) {
    statement body;
}
The initialization, condition, iteration and statement body components of a for statement are all optional.

The following will create an infinite for loop :
for (;;) {...} // Infinite loop.

The following will create a for loop with no body:
for (int i=1; i<5; i++); // No body

The initialization component is generally an assignment statement that sets the initial value of a control variable used to control iteration of the loop.

The condition component is a conditional expression that is always tested againt the control variable for true before each iteration of the loop. So if this is false to begin with then any statement body component will never be executed.

The iteration component is an expression that determines the amount the control variable is changed for each loop iteration.

The statement body is executed each time the condition component returns true when tested againt the control variable.
enhanced for loop
for (declaration : expression) {
    statement body
}
The declaration component declares a variable of a type compatible with the collection to be accessed which will hold a value the same as the current element within the collection.

The expression component can be the result of a method call or an expression that evalutes to a collection type.

The statement body is executed each time an element of the collection is iterated over.

Example usage of the different forms of the for statement is shown in the Beginning Java - Loop Statements lesson.

The while StatementTop

The while statement can be used to loop through a section of code while a condition remains true. The while statement has two different variations known commonly as the while loop and the do-while loop.

The following table shows the different forms of the while construct that can be used. We are using blocks of code to wrap statements which is optional when using a single statement, but good practice and will be used here.

Construct Description
while loop
while (condition) {
    statement body;
}
The condition can be any expression that results in a boolean and the loop will continue while the expression remains true, processing the statement body on each pass.

When the condition returns false the loop ends and control is passed to the next line following the loop.

Therefore if the condition starts as false the loop will never be entered.
do while loop
do {
    statement body;
} while (condition);
Unlike the normal while loop the statement body is processed before the condition is tested. The condition can be any expression that results in a boolean and the loop will continue while the expression remains true.

When the condition returns false the loop ends and control is passed to the next line following the loop.

Therefore even if the condition starts as false the loop will always execute at least once.

while Rules and Examples

There are a few rules to remember when coding the while and do while statements:

  • The expression we check against, in the parentheses, must evaluate to a boolean type: true or false.
  • When coding the while statement remember that the loop will never be executed if the expression we check against, in the parentheses resolves to false on loop entry.
  • When coding the do while statement remember that the loop will always be executed at least once, regardless of whether the expression we check against, in the parentheses resolves to false on loop entry.

Following are some examples showing while and do while statement usage:


/*
  Some code showing while and do while statement usage
*/
boolean b = true;
int i = 2;

 // while
while (b) {
    System.out.println("This is executed");
    break;
}   

while (i == 2) {
    System.out.println("This is executed");
    break;
}   

while (i) { // Will not compile, does not evaluate to boolean
    System.out.println(" ");
}   

while (i = 2) { // Assignment not evaluation, so won't compile
    System.out.println(" "); 
}   

while (true) { // This is fine
    System.out.println("This is executed");
    break;
} 

while (true) { // This is fine but loops endlessly
    System.out.println("This is executed");
} 
  
// do while
do {
    System.out.println("This is executed once"); 
} while (false);

do {
    System.out.println("This is executed once"); 
    b = false;
} while (b);

Example usage of the different forms of the while statement is shown in the Beginning Java - Loop Statements lesson.

Using the Assertion mechanism has no 'hit' on performance as we enable assertions at runtime using the -ea option. Compilation of a source file that uses assertions is the same as normal and any assertion code is ignored at runtime in default mode unless the file is run using the -ea option. So as an example:


javac OurProgram.java // Compile normally
java OurProgram // Run normal default mode
java -ea OurProgram // Run enable assertions mode

We would compile and run the OurProgram class in normal default mode. Then we would run the program with assertions enabled, and assertion code would be invoked as part of the block of code it belongs to. The assertion code leaves no footprint and is just ignored when the class is run without the -ea option.

Using the assert KeywordTop

The following table shows the two forms of the assert statement which are only active when running our code with the -ea option. The second form just allows us to pass more information to an AssertionError and we must ensure that the expression returns a value.

Assert Form Example Description
form1
assert condition;

// Other code
assert (a > 0);Throw AssertionError if condition equates to false

Run this code when assertion equates to true.
form2
assert condition: expression;


// Other code
assert (a > 0): "a = " + a;Throw AssertionError if condition equates to false passing the value returned from expression.

Run this code when assertion equates to true.

Appropriate Assertion UsageTop

Assertions should not be used to validate command-line arguments. We would have to run the code everytime using the -ea option to ensure assertions run the validation. Using exceptions is more appropriate for validating command-line arguments as these run regardless of deployment and the use of the -ea option.

Assertions should never be used that cause side-effects such as changing a value used elsewhere in the code. If we do so we are reliant on code being run using the -ea option to get the values changed. What happens when someone else runs the code without assertions enabled! Unreliable results and a difficult bug to find, that isn't really a bug, just an incorrect use of the assertion mechanism.

When considering whether to use assertions to validate arguments to a method, we need to consider the access modifier of the method:

  • Methods marked as public are not considered appropriate for assertions; public methods are available to anyone and should be robust enough to guarantee the validation and robustness of their interfaces. Using exceptions is more appropriate for public methods as these run regardless of deployment and the use of the -ea option.
  • Methods marked as protected are not considered appropriate for assertions; protected methods are available to subclasses outside the package and so should be robust enough to guarantee the validation and robustness of their interfaces. Using exceptions is more appropriate for protected methods as these run regardless of deployment and the use of the -ea option.
  • Methods with no access modifier are appropriate for assertions if you control the package; package-private methods are available only to programs within the package they belong to. If you have control then you can be reasonably assured that logic calling your protected method is correct.
  • Methods marked as private are considered appropriate for assertions as you control the code that calls the method; private methods are available only to the class they are written in. Therefore you have control and can be reasonably assured that logic calling your private method is correct.

Assertions should be used anywhere in code when you have code that should never be reached, such as a switch default statement, that you know will never be invoked. In these situations you set the assertion mechanism to false rather than test a condition. This ensures that if the statement is ever reached, when assertions are enabled, we will get an AssertionError.

See the Flow Control - Using Assertions lesson for example usage.

Exception OverviewTop

An exception refers to an exceptional condition that has occurred to alter the normal flow of our programs. When an exception occurs it will get thrown by the JVM or we can determine conditions to throw an exception ourselves when required. We can catch thrown exceptions and the code we write for this purpose is known as an exception handler. We can also declare exceptions in our method definitions, when we know for instance that the method will try to read a file that may or may not be present.

At the top of the exception hierarchy is the Throwable class, which all other error and exception classes inherit from. The Throwable class creates a snapshot of the stack trace for exceptions when they occur and may also contain information about the exception. We can use this information for debugging purposes. Only instances of the Throwable class, or subclasses of it, can be thrown by the JVM or be thrown by us.

Two subclasses inherit from the Throwable class and these are the Error and Exception classes. These classes, and subclasses thereof, are used to create an instance of the appropriate error or exception along with information about the exception:

  • The Error class and its subclasses handle error situations from within the JVM itself and so are outside our control as programmers. As a general rule we can't recover from an Error situation and because of this we are not expected to handle an Error when it occurs.
  • The Exception class and its subclasses are what concern us more as programmers and these can be split into two categories:
    1. Runtime exceptions: Are exceptions that are not checked for by the compiler and for this reason are also known as unchecked exceptions. Generally exceptions of type RuntimeException originate from problems in our code, such as an attempt to divide by zero which gives an ArithmeticException . These sorts of errors are within our control and as such should be handled by the code itself.
    2. Checked exceptions: Are exceptions that are checked for by the compiler and if present and not declared, will give a compiler error. An example of this is a FileNotFoundException where a file we want to read may or may not be available. Whether the file is available or not is outside our control. So when we read the file, we need to handle or declare that this exception, or a superclass of it may occur.

Exception Hierarchy DiagramTop

The diagram below is a representation of the exception hierarchy and by no means a complete list, but should help in visualisation:

exception hierarchy

In the diagram above each box represents a class and as you can see the Exception class, which interests us as programmers, has lots of subclasses attached to it. We are just showing some of them in the above diagram as space permits, but I am sure you get the idea. When an exception happens an exception object of the appropriate class is created and we can use code to handle the exception.

try catch finally  ConstructTop

The following table shows the different forms of the try catch finally construct.

Construct Description
try catch
try {
    // Code to be monitored for exception goes here
}
catch (Exception ex) {
    // Code to be executed on exception
}

Execute statements in try code block.


Execute statements in catch code block.
try catch finally
try {
    // Code to be monitored for exception goes here
}
catch (Exception ex) {
    // Code to be executed on exception
}
finally () {
    // Code to be run whether exception or not
}

Execute statements in try code block.


Execute statements in catch code block


Execute statements in finally code block.
try finally
try {
    // Code to be monitored for exception goes here
}
finally () {
    // Code to be run whether exception or not
}

Execute statements in try code block.


Execute statements in finally code block.
try with multiple catch
try {
    // Code to be monitored for exception goes here
}
catch (Exception1 ex1) {
    // Code to be executed on exception 1
}
catch (Exception2 ex2) {
    // Code to be executed on exception 2
}
...
catch (ExceptionN exN) {
    // Code to be executed on exception N
}

Execute statements in try code block.


Execute statements in catch code block 1.


Execute statements in catch code block 2.



Execute statements in catch code block N.
try with multiple catch and a finally
try {
    // Code to be monitored for exception goes here
}
catch (Exception1 ex1) {
    // Code to be executed on exception 1
}
catch (Exception2 ex2) {
    // Code to be executed on exception 2
}
...
catch (ExceptionN exN) {
    // Code to be executed on exception N
}
finally () {
    // Code to be run whether exception or not
}

Execute statements in try code block.


Execute statements in catch code block 1.


Execute statements in catch code block 2.



Execute statements in catch code block N.


Execute statements in finally code block.

try catch finally  Rules

When using the try catch finally construct there are certain rules that must be adhered to or you get a compiler error:

  • When using a try block it must be accompanied by a catch block, a finally block or both.
  • When using a catch block it must immediately follow the try block.
  • When using multiple catch blocks they must go in order from the most specific error to the most generic as discussed in Exceptions And Polymorphism.
  • When using a finally block it must immediately follow the last catch block, or the try block when no catch block is present.

Declaring ExceptionsTop

We can use the throw   keyword to throw exceptional conditions from within our code. At the top of the exception hierarchy is the Throwable class, which all other error and exception classes inherit from. So we can throw any subtype of the Throwable class if we want to, which includes objects of type Error, Exception and RuntimeException. The compiler demands that we either handle or declare exceptions which are neither error or runtime exceptions, in other words checked exceptions. See the Declaring Exceptions lesson for example usage.

Overridden Methods & Exceptions

There are a few things to remember when overriding methods that throw exceptions:

  • An overriding method can throw any Error or RuntimeException exceptions, whether these are declared in the overridden method or not.
  • An overriding method must not throw any new checked exceptions or any checked exceptions that are higher up the inheritance tree than those declared in the overridden method.
  • An overriding method can throw checked exceptions that are lower in the inheritance tree than those declared in the overridden method, or throw fewer or none of the checked exceptions that were declared in the overridden method.

Certification Exceptions & ErrorsTop

The following table lists the exceptions and errors we need to know for the certification:

Error/Exception Description
ArrayIndexOutOfBoundsExceptionAttempt to access array with an illegal index.
An example of this exception is shown in the Arrays lesson when we look at java.lang Array Exceptions.
ClassCastExceptionAttempt to cast an object to a subclass of which it is not an instance.
An example of this exception is shown in the Generics lesson when we look at a Raw Type/Generic Type Comparison.
IllegalArgumentExceptionMethod invoked with an illegal argument.
IllegalStateExceptionMethod invoked while an application isn't in the correct state to receive it.
NullPointerExceptionAttempt to access an object with a null reference.
An example of this exception is shown in the Reference Variables lesson when we look at The Heap.
NumberFormatExceptionInvalid attempt to convert the contents of a string to a numeric format.
An example of this exception is shown in the Exception Handling lesson when we look at Using  try catch finally.
AssertionErrorAssertion boolean test returns false.
An example of this exception is shown in the Using Assertions lesson when we look at Using the assert Keyword.
ExceptionInInitializerErrorAn unexpected exception has occurred in a static initializer.
StackOverflowError A stack overflow occurs because an application recurses too deeply.
NoClassDefFoundErrorTry to load in a class that can no longer be found.

Related java Tutorials

Beginning Java - Conditional Statements
Beginning Java - Loop Statements
Beginning Java - Primitive Variables
Beginning Java - Operators
Flow Control - Using Assertions
Flow Control - Exception Overview
Flow Control - Exception Handling
Flow Control - Declaring Exceptions

go to home page Homepage go to top of page Top