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
- Develop code that implements an if or switch statement; and identify legal argument types for these statements.
- 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.
- Develop code that makes use of assertions, and distinguish appropriate from inappropriate uses of assertions.
- Develop code that makes use of exceptions and exception handling clauses (try, catch, finally), and declares methods and overriding methods that throw exceptions.
- 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.
- Recognize situations that will result in any of the following being thrown:
ArrayIndexOutOfBoundsException,ClassCastException,IllegalArgumentException,IllegalStateException,NullPointerException,NumberFormatException,AssertionError,ExceptionInInitializerError,StackOverflowErrororNoClassDefFoundError. Understand which of these are thrown by the virtual machine and recognize situations in which others should be thrown programmatically.
- Develop code that implements an if or switch statement; and identify legal argument types for these statements.
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) { | Execute statements in statement1 if condition expression evaluates to true. |
if....else | |
if (condition) { | Execute statements in statement1 if condition expression evaluates to true,otherwise execute statements in statement N. |
Nested if | |
if (condition) { | Execute statements in statement1 if condition expression evaluates to trueExecute 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) { | 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
booleantype:trueorfalse. - One optional
elsestatement can be coded as part of theifconstruct and must come after anyelse ifstatements. - Multiple
else ifstatements can be coded after the initialifand before the optionalelsestatement (if coded). - As soon as we hit a
truecondition any otherelse ifstatements 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) { |
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) { |
This is perfectly legal. |
switch Rules and Examples
There are a few rules to remember when coding the switch statement:
- The
switchexpression we check against, in the parentheses, must evaluate to achartype, or thebyte,shortorintsigned numeric integertypes or to anenum. - When not using an
enumin ourswitchstatement only types that can be implicitly cast to anintare allowed. - The
switchstatement can only check for equality (==) and so the otherrelational operatorswon't work withswitch. - The
caseconstant must evaluate to the same type as theswitchexpression. - The
caseconstant 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
caseconstants must be different. - When a
caseconstant evaluates totruethe associated code block and any code blocks following are executed, unless abreakstatement is encountered. - The
defaultstatement can be placed anywhere within theswitchstatement and when executed, the associated code block and any code blocks following are executed, unless abreakstatement 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]) { | 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 bodyThe 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) { | 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) { | 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 { | 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
booleantype:trueorfalse. - When coding the
whilestatement 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 whilestatement 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 falseRun 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
publicare not considered appropriate for assertions;publicmethods are available to anyone and should be robust enough to guarantee the validation and robustness of their interfaces. Using exceptions is more appropriate forpublicmethods as these run regardless of deployment and the use of the-eaoption. - Methods marked as
protectedare not considered appropriate for assertions;protectedmethods 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 forprotectedmethods as these run regardless of deployment and the use of the-eaoption. - Methods with no access modifier are appropriate for assertions if you control the package;
package-privatemethods are available only to programs within the package they belong to. If you have control then you can be reasonably assured that logic calling yourprotectedmethod is correct. - Methods marked as
privateare considered appropriate for assertions as you control the code that calls the method;privatemethods are available only to the class they are written in. Therefore you have control and can be reasonably assured that logic calling yourprivatemethod 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
Errorclass 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 anErrorsituation and because of this we are not expected to handle anErrorwhen it occurs. - The
Exceptionclass and its subclasses are what concern us more as programmers and these can be split into two categories:- 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
RuntimeExceptionoriginate from problems in our code, such as an attempt to divide by zero which gives anArithmeticException. These sorts of errors are within our control and as such should be handled by the code itself. - 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
FileNotFoundExceptionwhere 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.
- 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
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:
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 { |
Execute statements in try code block.Execute statements in catch code block. |
try catch finally | |
try { |
Execute statements in try code block.Execute statements in catch code blockExecute statements in finally code block. |
try finally | |
try { |
Execute statements in try code block.Execute statements in finally code block. |
try with multiple catch | |
try { |
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 { |
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
tryblock it must be accompanied by acatchblock, afinallyblock or both. - When using a
catchblock it must immediately follow thetryblock. - When using multiple
catchblocks they must go in order from the most specific error to the most generic as discussed in Exceptions And Polymorphism. - When using a
finallyblock it must immediately follow the lastcatchblock, or thetryblock when nocatchblock 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
ErrororRuntimeExceptionexceptions, 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 |
|---|---|
ArrayIndexOutOfBoundsException | Attempt 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. |
ClassCastException | Attempt 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. |
IllegalArgumentException | Method invoked with an illegal argument. |
IllegalStateException | Method invoked while an application isn't in the correct state to receive it. |
NullPointerException | Attempt 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. |
NumberFormatException | Invalid 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. |
AssertionError | Assertion boolean test returns false.An example of this exception is shown in the Using Assertions lesson when we look at Using the assert Keyword. |
ExceptionInInitializerError | An unexpected exception has occurred in a static initializer. |
StackOverflowError | A stack overflow occurs because an application recurses too deeply. |
NoClassDefFoundError | Try 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