break
command. This is well illustrated in an example which is a simple variation
on the last average program, Average5.java
which exited when a
sentinel value was read. Here is the new version:
1 import java.io.*; 2 3 class Average6 4 { 5 // A program that calculates the average of a series of integers. 6 // It prompts for each one. A sentinel value is used to indicate the 7 // end of the series. 8 9 static final int SENTINEL = -999; 10 11 public static void main(String[] args) throws IOException 12 { 13 BufferedReader in = Text.open(System.in); 14 int sum=0,count=1,n; 15 while(true) 16 { 17 System.out.print("Enter number "+count+" (or "+SENTINEL+" to finish): "); 18 n=Text.readInt(in); 19 if(n==SENTINEL) 20 break; 21 sum+=n; 22 count++; 23 } 24 System.out.println("Average is: "+Text.writeDouble((double)sum/(count-1),5,3)); 25 } 26 }As you can see, the condition in the while loop is simply the boolean constant
true
, which obviously always has the value true
. As
it can never become false
the loop can never be exited in the
normal way by failing the loop condition. It can however be exited by the
break
on line 20. A break
simply means execution
must immediately leave the loop (whether it is a while, for or do loop) which
it is within. If it is in a loop within a loop, it only leaves the innermost
one (there is a form of break
known as a labelled break
which can be used to make execution leave more than one loop in one go).
A switch
statement is treated like a loop for the purpose of
exit by break
, indeed it has to in order to avoid the follow-on
between cases. However, an if
statement is not treated as the
statement to be exited, it is the switch or loop which encloses it which is
exited. Again, that has to be the case, since if a break
were
not within an if
statement within a loop it would always be
encountered on every execution of the loop, and the loop would never be
repeated.
In the Average6
example above, lines 19-20 make up the
if
statement, so the break
statement within in is
only executed if the value in variable n
is equal to the
sentinel value. In fact this gives us precisely the effect we want here -
a loop where the real exit is in the middle. It ends the awkwardness of
Average5
where we read the next integer at the end of the loop,
and added it at the beginning, with an extra integer read before the loop
to start off.
In general, break
should be used with caution. It is easier to
see where a loop exits if its exit point is always on failing the loop
condition, rather than through a break
which may occur at any
point in its body. break
may be used within non-infinite
loops (i.e. ones which do not have true
as their condition),
but using them within an infinite loop does at least signal that exiting
the loop must be done somewhere in its body, while a break
inside a non-infinite loop is very easily missed as an alternative from of
exit. Note that as an alternative to while(true)
, the loop
heading for(;;)
is often used to mean an infinite loop. It
means the same, but for(;;)
perhaps jumps out more to the
eye, indicating immediately that we are dealing with an infinite loop,
than does while(true)
.
Rarely seen, but a valid part of Java, inherited from C, is continue
which works similarly to break
, but instead of exiting the loop
ignores any further execution of the body and starts again at the first
statement of the loop.
nums1
, is found in the
/import/teaching/BSc/1st/ItP/averages
directory. Note that it
has to contain the sentinel value to indicate end-of-data. A simple way to
redirect input to make it from a file is to do so at Unix level, using
Unix's input redirection facility. Assuming file Average6.java
has been compiled, if the Java interpreter is run with the Unix command:
java Average6 < nums1it will take its input from the file
nums1
(this file must be
in the same directory, of course) rather than from what is typed in at the
command window. In general, any Unix command can be made to take its
input from some file by adding < filename
to it, where
filename
is the name of the file in question. Output direction
is similar - adding > filename
to the end of a Unix command
causes output to be redirected to the file named filename
.
Note that this output redirection will create a new file of the given name is
one doesn't exist already, and it will overwrite i.e. destroy all
the existing information in a file if one of that name exists already.
However, if you want output redirection to append to an exiting file rather
than overwrite it, all you do is use >> filename
, i.e. with
a double greater than sign. You may combine input redirection and output
redirection in one Unix command.
Running Average6
with input redirection, however, has a
silly effect. The prompts, that made sense when it was the user typing
in the numbers in response to them, are still printed, even though it makes
no sense to have them when the information is not being entered interactively.
So if we are intending our program to be used for input redirected from a
file, we should not have any prompts. The file Average7.java
contains a program which is just like Average6
, but lacks the
prompt on line 17 of Average6.java
(and is commented to
indicate this is so).
1 import java.io.*; 2 3 class Average8 4 { 5 /* 6 A program that calculates the average of a series of integers read 7 from a file. A sentinel value is used to indicate the 8 end of the series. 9 The program prompts for the name of the file the numbers are to be read 10 from. 11 */ 12 13 static final int SENTINEL = -999; 14 15 public static void main(String[] args) throws IOException 16 { 17 BufferedReader in = Text.open(System.in); 18 BufferedReader fileReader; 19 String filename; 20 int sum=0,count=0,n; 21 System.out.print("Enter file name to read from: "); 22 filename=Text.readString(in); 23 fileReader=Text.open(filename); 24 n=Text.readInt(fileReader); 25 while(n!=SENTINEL) 26 { 27 sum+=n; 28 count++; 29 n=Text.readInt(fileReader); 30 } 31 System.out.println("Average is: "+Text.writeDouble((double)sum/count,5,3)); 32 } 33 }This works as
Average6
, it shouldn't be used with input direction,
but instead takes input from a file whose name is typed by the user, in
response to a prompt, when the program is run. The way it works is to
open an additional BufferedReader
which is attached to the
file rather than to the "standard input" (i.e. input from the command window
or from command window redirection). In the examples you have seen so far,
you have always had just one BufferedReader
called in
attached to the standard input using a declaration like that in line 17 in
Average8
. In Average8
the second
BufferedReader
is declared on line 18, where it is given the
name fileReader
, but it isn't given a value and hence
attached to anything until line 23. As you can see from line 23, the
open
method from class Text
can take the name of
a file given in an ordinary string, and return a BufferedReader
attached to the file with that name. Then, if that BufferedReader
is given as the argument to the various reading methods in Text
in the place of in
which we have always used up till now,
they will read from that file rather than from the standard input. So line
29 shows readInt
with the variable fileReader
as its argument, meaning that it will read an integer from the file to
which fileReader
was attached in line 23.
Average8
will run fine if the file that is named
at the prompt exists, and if it contains the sentinel value -999
.
It will stop reading the input when it comes across the first -999
.
A file called nums2
in the averages
directory can be
used to demonstrate that - the numbers after -999
are ignored.
Given the behaviour of Text
, it can cope with files that don't
just contain integers, though it will print out the message Error in
number, try again
for each word it encounters - try it on file
nums3
. This message is really more appropriate for interactive
input. However, if in response to the prompt for a file name you type in
one that does not exist, say nums0
, the program will stop and the
following will be printed in the command screen:
java.io.FileNotFoundException: nums0 at java.lang.Throwable.If you type in the name of a file that exists, but does not contain the sentinel value(Compiled Code) at java.lang.Exception. (Compiled Code) at java.io.IOException. (Compiled Code) at java.io.FileNotFoundException. (Compiled Code) at java.io.FileInputStream. (Compiled Code) at java.io.FileReader. (Compiled Code) at Text.open(Compiled Code) at Average8.main(Compiled Code
-999
(an example exists with name
nums4
in the averages
directory) the program will
stop and the following will be printed in the command screen:
java.io.EOFException at java.lang.Throwable.What we have here are two different examples of exceptions. As mentioned briefly previously, exceptions are when execution hits something it was not prepared for. In the first case above, that was trying to open a file that does not exist. In the second case above, that was trying to carry on reading from a file when the end of it had been reached (remember the loop makes it read till it reads -999, so that's what happens when there is no -999 in the file).(Compiled Code) at java.lang.Exception. (Compiled Code) at java.io.IOException. (Compiled Code) at java.io.EOFException. (Compiled Code) at Text.refresh(Compiled Code) at Text.readInt(Compiled Code) at Average8.main(Compiled Code
Java, however, has a way of constructing programs so they can "expect the
unexpected". It is the try
construct. The try
construct takes the form
try { Java
statements }
followed by at least one catch
clauses. Note that the {
and }
are compulsory even
if there is only one Java statement between them. A catch
clause
takes the form
catch(exception
type exception
name) { Java
statements }
The exception
type
is the name of the
particular type of exception the catch
clause deals with. The
type of exception where an attempt is being made to read past the end of a
file is called a java.io.EOFException
while the type of
exception where an attempt is made to access a file that does not exist is
called a java.io.FileNotFoundException
. You can miss off the
java.io.
bit in any file which has the line
import.java.io.*;
in it. As you can see, these are the names
that were in the run-time error messages. The
exception
name
is the name of a
variable which holds the details of the particular occurrence of the exception.
It can be any name, at this point we won't make use of it. A try
construct may also have an extra finally
part after its
catch
clauses, but we won't consider that here.
The effect of a try
construct is that the Java statements
between the brackets of the try
bit are executed as normal,
but if an exception occurs anywhere in the execution, rather than the program
just halting, the exception may be caught by one of the
catch
clauses. If an exception is of the type indicated in one
of the catch
clauses, execution of the code inside the
try
bit stops, but is followed by execution of the code in the
relevant catch
clause. Following this, program execution continues
with the next statement after the whole try
construct, and then
as normal, rather than halting.
The following program is a variant of Average8
updated to
deal with the two exceptions mentioned above:
1 import java.io.*; 2 3 class Average9 4 { 5 /* 6 A program that calculates the average of a series of integers read 7 from a file. A sentinel value is used to indicate the 8 end of the series. 9 The program prompts for the name of the file the numbers are to be read 10 from. 11 */ 12 13 static final int SENTINEL = -999; 14 15 public static void main(String[] args) throws IOException 16 { 17 BufferedReader in = Text.open(System.in); 18 BufferedReader fileReader; 19 String filename; 20 int sum=0,count=0,n; 21 System.out.print("Enter file name to read from: "); 22 filename=Text.readString(in); 23 try 24 { 25 fileReader=Text.open(filename); 26 n=Text.readInt(fileReader); 27 while(n!=SENTINEL) 28 { 29 sum+=n; 30 count++; 31 n=Text.readInt(fileReader); 32 } 33 } 34 catch(EOFException e) 35 { 36 System.out.println("No sentinel "+SENTINEL+" found in file"); 37 } 38 catch(FileNotFoundException e) 39 { 40 System.out.println("File "+filename+" not found"); 41 } 42 System.out.println("Average is: "+Text.writeDouble((double)sum/count,5,3)); 43 } 44 }The complete
try
construct extends over lines 23 to 41. Line 42
is the statement after the try
construct. If during
the execution of lines 25-32 an EOFException
occurs, the
execution of those lines is halted, and line 36 is executed, followed by line
42, and execution would continue as normal if there were any more
statements after line 42. If during the execution of lines 25-32 a
FileNotFoundException
occurs, the execution of those lines is
halted, and line 40 is executed, followed by line 42, and execution would
continue as normal if there were any more statements after line 42.
In both catch
clauses the exception deatils are put in
a variable named e
, but the variable is not used further.
In this case the effect is that if no sentinel is found a message saying that
is printed, but the average is calculated anyway since we have correctly
counted how many numbers there are in the file and added them. The
EOFException
could occur (or to use correct Java terminology,
be thrown) on attempting to execute line 26 or line 31 (it would only
occur from line 26 if there were no numbers at all in the file) and coming
against the end of the file being read.
The FileNotFoundException
could only be thrown from line 25. It
would result in line 40 being executed, printing out the message there
which includes the name that was typed in but found not to refer to an
openable file. Following this, line 42 would still be executed, so given that
count
would hold zero, the question mark indicating an attempt
to use writeDouble
with the result of a division by zero
would be printed.
Note that if an exception is thrown in a method which was called by another
but there is nothing to catch it there, it could be caught in the method
which called it, and if there is a chain of methods which called each other,
the exception would be thrown from each one until it was caught or until it
was thrown by main
. Only when main
throws an
exception does the program halt and print an error message in the command
window.
Now you've seen exceptions, you can see the relevance of the
This program also shows an example of a
These notes were produced as part of the course
Introduction to Programming as it was given in the
Department of Computer Science at
Queen Mary, University of London
during the academic years 1998-2001.
Last modified: 2 Oct 1998
throws
part of a method header. Exceptions actually come in two sorts:
run-time exceptions and checked exceptions.
Run-time exceptions can be dealt with if the programmer wishes or
the possibility of them occurring can be ignored. Checked exceptions
however, have to be dealt with somehow. If there is no try
construct to catch one in a place where it might occur, the method where it
might occur has to indicate that it can throw the exception. For example,
any input can cause an IOException and an
IOException
is a checked exception, so a method that does input but does not catch
IOExceptions
must have throws IOException
attached
to its header.
Using exceptions for control flow
You can see that in Average9
there is really no need for
a sentinel. If we can correctly add up the numbers when the end of file is
reached, why not write the program so this is the standard: read the numbers
until the end of file is reached? This is done in the program below:
1 import java.io.*;
2
3 class Average10
2 {
4 /*
5 A program that calculates the average of a series of integers read
6 from a file. The integers are read until the end of file is encountered.
7 The program prompts for the name of the file the numbers are to be read
8 from.
9 */
10
11 public static void main(String[] args) throws IOException
12 {
13 BufferedReader in = Text.open(System.in);
14 BufferedReader fileReader;
15 String filename="";
16 int sum=0,count=0,n;
17 for(;;)
18 {
19 try
20 {
21 System.out.print("Enter file name to read from: ");
22 filename=Text.readString(in);
23 fileReader=Text.open(filename);
24 break;
25 }
26 catch(FileNotFoundException e)
27 {
28 System.out.println("File "+filename+" not found");
29 }
30 }
31 try
32 {
33 for(;;)
34 {
35 n=Text.readInt(fileReader);
36 sum+=n;
37 count++;
38 }
39 }
40 catch(EOFException e)
41 {
42 System.out.print("Average is: "+Text.writeDouble((double)sum/count,5,3));
43 System.out.println();
44 }
45 }
46 }
In this program, lines 33-38 is an "infinite loop", but it always exits as
line 35 will eventually try to read past the end of file, cause an
EOFException
, and cause execution to go to line 42 after the
EOFException
is caught on line 40. So we have here an infinite
loop inside a try
construct.
try
construct
inside an infinite loop. The loop takes up lines 17-30. This is used to
keep on prompting for a file name and attempting to open a file of the name
typed in until a file is successfully opened. The loop is exited by the
break
on line 24. Note that break
exits from the
loop and not just from the try
construct. Execution of the body
of the loop on lines 17-30 starts with line 21. If a file is successfully
opened on line 23, it goes on to line 24, executes the break
and
then exits the loop, going on to line 31 where it starts reading from the
file. If, however, line 23 does not successfully open a file, a
FileNotFoundException
is thrown, and is caught on line 26.
This then causes line 28 to be executed. Since the break
on
line 24 was never executed, execution continues with the start of the loop
again.
Matthew Huntbach