Flow Control, Assertions and Exception Handling
In this section you should be able to:
1. Write code using if and switch
statements and identify legal argument types for these statements.
2. Write code using all forms of loops including
labelled and unlabeled, use of break and continue, and state the values taken by loop counter
variables during and after loop execution.
3. Write code that makes proper use of exceptions and
exception handling clauses (try, catch, finally) and declares methods and overriding methods that
throw exceptions.
4. Recognise the effect of an exception arising at a
specified point in a code fragment. Note: The exception may be a runtime
exception, a checked exception, or an error (the code may include try, catch,
or finally clauses in any legitimate combination).
5. Write code that makes proper use of assertions, and
distinguish appropriate from inappropriate uses of assertions.
6. Identify correct statements about the assertion
mechanism.
Flow
Control, Assertions and Exception Handling
The while() loop
while(expression)
{
//statements
} //end of while
loop
The expression must evaluate to a boolean true or false result. If
it is true then the statements within the code block will be executed, this
will continue to repeat until a false result is evaluated. If or once it is
false then the statements within the code block will be skipped and control
will be passed back to the method which called it.
If no curly braces are used to form a code
block then only the first statement after the expression is treated as the
statement block.
The do() loop
do
{
//statements
}while(expression);
The do loop (or do..while loop as it’s sometimes known) is much the same
as the while loop however in this loop the statement block is executed first
before the expression is evaluated. Once again the expression must evaluate to
a boolean true or false
result. If it is true then the statements within the code block will be
executed, this will continue to repeat until a false result is evaluated.
It is
important to remember that within a loops statement block there is code that
will affect the evaluation of the expression. Failure to do this will result in
an infinite loop, which results in locking up the program.
The most complicated yet versatile loop
construct is,
The for() loop
for(initialisation; expression; increment)
{
//statements
} //end of for
statement
Part |
Description |
initialisation |
Also referred to as the start condition.
It is used to assign an initial value to an identifier normally called the
counter in order to start the loop. The loop can start at any value. |
expression |
The expression normally consists of a
relational test between the counter and a maximum or minimum value that
related to the number of iterations the loop needs to perform. This is often
the terminating condition of the loop. The expression needs to evaluate to a
Boolean true or false, and does not necessarily need to relate to the
counter. The first time the loop is entered the
condition is tested after the initialisation part but before the statement
block is entered. If this initial test evaluates to false then the statement
block will not be executed. |
increment |
The increment part is executed after each iteration of the statement block, but before
the condition is re-tested. The increment part can increase or
decrease the counter by a set amount. |
All three parts of the for()
construction are optional. If the test is missing, it is treated as perpetually
true. If no curly braces are used to form a code block then only the first
statement after the expression is treated as the statement block.
Break and continue
The break statement abandons the loop altogether; the test is
not performed on the way out.
The continue statement causes the current iteration of the loop
to be abandoned. Flow restarts at the bottom of the loop. For while() and do,
this means the test is executed next. For the for() loop, the third
statement in the parenthesis is executed, followed by the test.
Both break
and continue can take a label
that causes them to skip out of multiple levels of nested loop. The matching
label must be placed at the head of a loop and is indicated by using an
identifier followed by a colon (:). For example
public class test
{
public static void
main(String[] args)
{
outer: for(int i=0; i<2;
i++)
{
for(int j=0; j<3; j++)
{
if(i==j)
{
continue
outer;
}
System.out.println("i = " + i + " j =
" + j );
}
}
}
} //end of test
class
The above code produces the following
output.
When i
and j have the same value, the continue
keyword sends the runtime back to the outer loop as specified, before the output is generated.
Because the outer loop is the target of the continue
statement, the whole of the inner loop is abandoned. So, for the value pairs,
this table shows what happens:
i |
j |
Effect |
0 |
0 |
continues to outer loop |
1 |
0 |
prints output |
1 |
1 |
continues to outer loop |
2 |
1 |
exits loops (as this is the point where
the expression in the for loop returns false) |
Selection
Statements
The if statement
The if statement is used to make decisions.
if( expression )
{
//statements
} //end of if
statement
Where expression refers to any expression that returns a boolean true or false value. The
statement will only be executed if the expression evaluates to true. If no
curly braces are used to form a code block then only the first statement after
the expression is treated as the statement block.
The if…else statement
The basic if statement can be
extended by adding the else clause.
if( expression )
{
//statements_1
}
else
{
//statements_2
} //end of
if…else statement
The
multiple if…else statement
if( expression )
{
//statements_1
}
else if( expression )
{
//statements_2
}
else
{
//statements_3
} //end of multiple
if statement
Nested
if statements
if( expression )
{
if( expression )
statement;
} //end of nested if
statement
The switch statement
Java™ provides us with another
selection statement. The switch statement evaluates the expression and compares it
against each of the case constants in turn. Unlike the if statement, the switch
statement does not need to use statement blocks for each case. Execution begins
at the appropriate label and continues until the break statement.
switch( expression )
{
case constant1: statement1;
….
statementn;
break;
case constant1: statement1;
….
statementn;
break;
case constant1: statement1;
….
statementn;
break;
default: statement1;
….
statementn;
break;
} //end of switch
statement
The expression used in a switch
statement must be of an integral type or one whose value can be implicitly cast
into an int type such as byte, short, long or char.
The break statement identifies the end of a particular case
and causes immediate exit from the switch. It prevents execution from dropping through to the next case statement.
The default statement is a catch-all
statement in that if no constants match the expression then the statement list
associated with the default statement is executed. It is important to remember
that the default keyword is optional, it is
not a requirement of the switch statement. If used, convention dictates that it
usually appears at the end of a switch statement, but it is equally valid to put it at the
beginning.
Assertions
Since Java™ 1.4 there has been an
assertions facility.
Assertions provide a convenient mechanism
for verifying that a class’s methods are called correctly. This mechanism
can be enabled or disabled at runtime. The assert keyword has the
following syntax:
assert expression1;
assert expression1: expression2;
expression1 must have a Boolean type. expression2 may have any type. If assertions are disabled at
runtime (the default state), the assert statement does nothing. If it’s enabled at
runtime via command line argument) then expression1 is evaluated.
If
its value is true, no further action is taken. If it’s false then an AssertionError is thrown. If expression2 is present, it is passed into the constructor of the
AssertionError, where it is converted to a String
and used as the error’s message.
Compiling
with assertions
To compile a program with assertions enabled
compile with –source 1.4. For example if we had a class called Test
and we wanted to compile with assertions enabled we would use the following
command:
javac –source 1.4 Test.java
If the flag is omitted, the 1.4 compiler
treats source code as if the assert keyword did not exist.
Runtime
enabling of Assertions
Assertions are disabled by default. If we
want to use the assertions coded and compiled into our Test
class at runtime then we use the –enableassertions or –ea flag on the java command line, for example:
java –ea Test
The following is a Test class
public class test
{
public static void
main(String[] args)
{
int
age = 0;
assert(age>0):"Age
was not greater than zero. End";
System.out.println(“End of test class”):
}
} //end of test
class
If we compile and run with the assertions
enabled we get the following:
If we change the age to 5, compile and run
again, we get the following result:
No problems, the result of expression1 returned true so the code continued to run without interruption.
Exception
Handling
An exception is an object that is created
when something unexpected happens. For example:
·
Trying to access
an element in an array that does not exist.
·
Illegal
arithmetic operations (e.g. a division by zero).
·
Invalid data
received as input.
·
Trying to open a
file that does not exist.
·
Non-existent
network connections.
Class hierarchy for the Exception class and
some other classes of interest are shown below. It does not include all of the
exception classes – there are many more.
Using
try() catch(){}
A try block is a code block that is preceded with the
keyword try followed by a pair of braces, for example:
try
{
// code which might throw an exception
}catch(exception e)
{
// handle the exception here
}// end of try catch
block
The above try..catch block can be used to catch any type of exception
thrown in a program.
Multiple
catch blocks
Finally
The finally
statement can be added to the end of a try..catch block to ensure that a block of code is executed,
regardless of whether an exception was caught (or not caught) or thrown.
try
{
// code which might throw an exception
}catch(exception e)
{
// exception caught here
}finally
{
// finally block executed
}// end of try catch
block
Throwing
exceptions
The throw Statement
Most exceptions are thrown by the standard java.lang classes, but any programs can be made to throw
there own exceptions. The throw statement causes an exception to be thrown. You can create a throw statement two ways,
either by creating an exception and then throwing it at once or in two separate
statements. See example.
throw new ioexception(“file
does not exist”); // creates an ioexception and
throws it in one statement
ioexception
e = new ioexception(“file does not
exist”); //creates an ioexception
throw e; //the
exception is then thrown
The program below creates a new
exception that is thrown when the user runs the program and enters a negative
number into the command line arguments, for instance, -1.
The exception is caught by a catch
block containing a statement that uses the exception’s getMessage()
method to display the exception message.
public class test
{
public static void
main(String[] args)
{
try
{
float num
= Float.parseFloat(args[0]);
if( num
< 0 ) throw new IllegalArgumentException("Positive
numbers only");
}catch( IllegalArgumentException e )
{
System.out.println(e.getMessage());
}
}
} //end of test
class
If I enter a positive number of 10 it runs
fine, however if I enter -10, our exception is thrown and the catch
block picks it up.
As a general rule, Java™ requires that
any method that might throw an exception must declare the fact so that
responsibility for handling exceptions can be passed, from the method
containing the exception, to the method that is calling it.
To pass this responsibility the throws
keyword is added after the method declaration, followed by the name of the
predicted exception to be handled.
In the program below, if the user enters
“10” the program works fine, however if the user enters
“ten” an exception occurs in the calc() method but is handled
by the catch statement in the main method.
public class test
{
public static void
main(String[] args)
{
try
{
System.out.println("Number
is " + calc(args[0]) );
}
catch( NumberFormatException e )
{
System.out.println("Wrong
format: " + e.getMessage());
}
}
public static int calc(String num) throws NumberFormatException
{
int
number = Integer.parseInt(num);
return
number;
}
} //end of test
class
User
defined exceptions
Creating and using your own exceptions
brings convenience and power to Java™. The common practice is to extend
the exceptions class so that the new class will have access to all of the
methods which are available to the Exceptions class. In the following example I create my own
exception called TestException. The code below is in one file.
public class test
{
public static void
main(String[] args)
{
int
i = Integer.parseInt(args[0]);
try
{
testUserException(i);
}catch(TestException e)
{
System.out.println(e.getMessage());
}
}
public static void
testUserException(int val) throws TestException
{
if( val < 5 )
throw new
TestException("Number is less than five -
EXCEPTION thrown");
else
System.out.println("Number
greater than 5. Good.");
}
} //end of test
class
class TestException
extends Exception
{
TestException(String message)
{
super(message);
}
TestException()
{
super();
}
} //end of user
defined exception
If we run the program with i at a value of 6 the program runs fine. If we change
it to 3 then the method picks up the TestException and throws it back to the calling method (in this
case the main method) to deal with. This is shown by the command
window displaying the exception message. Methods can also be added to the TestException class if required.
Checked
and unchecked exceptions