Week 7 Notes and Questions
Exceptions
- Exceptions are part of the Java language will allow you to deal with
run-time errors. Exceptions are Objects, and there is a class heirarchy of
exceptions in the library, but they are special (in a similar way to the
String class), in that the Java language "knows" about them.
- In most 3GL languages, a routine may indicate that something went wrong by
returning a particular status code. What problems does this approach have?
- Have to agree on a convention for what status codes mean - on VMS you
use even values to mean failure, but UNIX (and Win32) are notorious for
having a variety of different returns (e.g. 1, 0, -1, NULL)
- How do you indicate that a function which is supposed to return an
integer has failed?
- What do you do for "void" functions?
- You can't force people to check return values. E.g. "printf" returns a
success/failure status but how many people check it?
- Exceptions are an attempt to deal with these issues. In Java, an
exception is "thrown" when some exceptional condition (usually an error)
occurs.
- The syntax to throw an exception is throw object, e.g. in the
Person constructor we might say:
if (Person.age < 0) {
throw new Exception("can't have -ve age");
}
- The "object" that is thrown must be of a class called Throwable.
Needless to say, Throwable is a base class which has many subclasses, of which
one is "Exception". Note that the Java language "knows" about the Throwable
class.
- What happens when an Exception is thrown? The JVM will look for a
suitable handler to catch the exception (this is analagous to condition
handlers on VMS).
- What if there is no handler to catch the Exception? Ans: there will be,
because the compiler will make sure that there is one. If you have code
which throws an Exception, the compiler will force you to either write a
handler, or state that the method itself can throw the exception
- To handle an Exception, use the catch clause. E.g.
try {
throw new Exception("silly exception");
}
catch (Exception e) {
System.out.println("I caught : " + e);
}
- To get someone else to handle the exception, use throws on your
method definition, e.g.
public Person(String name, int age) throws Exception
{
if (age < 0) {
throw new Exception("can't have -ve age");
}
}
- If you call a method that has a "throws" clause, then you then the onus of
handling any conditions it may throw falls to you. What does this mean?
Either you enclose your call to the method in a "try..catch", or you
stick a "throws" clause on your own method and pass the buck on.
- Either way, the compiler won't let you compile code which throws
exceptions unless it can see someone has assumed responsibility for catching
them.
- You can have as much code in a try..catch block as you like.
- There is a heirarchy of Exception classes. One important sub-heirarchy is
"RuntimeException". RuntimeExceptions are special because you don't
have to catch them. Anyone think of what kind of exceptions might be included
here? And why you'd have them? Ans: RuntimeExceptions are generally used for
situations caused by programming errors. For a programming error,
e.g. NullPointerException, you typically won't want to recover from that, and
you certainly don't want to have to write "try..catch" around every statement
that could ever cause a NullPointerException
- You are allowed to catch RuntimeExceptions if you want, but if you throw
them, or call a method which throws them, the compiler won't force you to
have a catch block
- When you have a "catch" block, the type of exception you catch should be
as specific as possible. You can have multiple "catch" statements, being less
and less specific, e.g.
try {
...
}
// only catch end of file exceptions, which is a subclass of IOException.
catch (EOFException e) { ... }
// any other IOExceptions apart from EOFException
catch (IOException e) { ... }
// catch any other Exception.
catch (Exception e) { ... }
- In any case, the compiler will make sure that you have a catch block
appropriate for the type of exceptions which can be thrown. E.g. if you call
a method which is declared throws EOFException, then you will be able
to get away with a catch of "EOFException" or any of its super-classes.
- Although the comiler does make you have a catch clause when you need one,
it won't always stop you from putting one in if you don't need one. I.e. the
following will compile:
try {
a = 1;
}
catch (Exception e)
{
System.out.println("I got an Exception");
}
- What can you do inside a "catch" block? You can deal with the exception,
or if you decide you can't fix it, then you can re-throw the exception:
try {
...
}
catch (EOFException e)
{
if (x == -1) {
throw e;
}
}
you could throw a different exception:
try {
...
}
catch (EOFException e)
{
if (x == -1) {
throw new MyException("can't cope with that");
}
}
In either of these cases, you'd have to have an outer "catch" block. Or if
you don't want one, you could use a RunTimeException"
try {
...
}
catch (EOFException e)
{
if (x == -1) {
throw new RuntimeException("program bug");
}
}
- As well as try and catch, there is another keyword used for
exception handling, which is finally. You don't have to have a
"finally" clause, but if you do, then it specifies code that will run
regardless of whether an exception was thrown.
- Typical use for "finally" is to do cleanup. For example, in this code, we
can be sure that the file we open at the start of the "try" block will always
get closed:
FileInputStream f = null;
try {
f = new FileInputStream("fred.dat");
...
}
catch (EOFException e)
{
...
}
finally {
// This will always be run, whether or not any exceptions
// happened
if (f != null) {
// "close" itself can throw exceptions, but we don't care
// what they are
try {
f.close();
}
catch (Exception e) {}
}
}
- You don't need to have a "catch" before a finally, i.e. you can have
try {
...
} finally {
...
}
Questions
Below is "Person.java" with all the javadoc comments deliberately not
included.
import java.awt.*; // for java.awt.Color
import java.util.*; // for java.util.Date
class Person implements Comparable {
int age;
String name;
Color hairColor;
final java.util.Date creationDate;
// Copy the values
public Person(String name, int age, Color hair)
{
this.name = name;
this.age = age;
this.hairColor = hair;
creationDate = new java.util.Date();
}
public Person(String name, int age)
{
this(name,age,null);
}
// "Factory" method
static public Person createBlonde(String name, int age)
{
return new Person(name,age,new Color(128,128,0));
}
public String toString()
{
return name + ", who is " + age + " and was created on " +
creationDate;
}
// Required for "Comparable" interface
public int compareTo(Object o)
{
if (o instanceof Person) {
Person other = (Person)o;
if (other.age > age) {
return 1;
}
if (other.age < age) {
return -1;
}
}
// The object wasn't a Person, or it was
// a Person with the same age. So we'll use
// the hashcode to make the decision
if (o.hashCode() == hashCode()) {
// They are the same object
return 0;
}
if (o.hashCode() > hashCode()) {
return 1;
}
return -1;
}
public static void main(String [] args)
{
java.util.LinkedList myCollection = new java.util.LinkedList();
for (int i=0; i<20; i++) {
// Create a new person with random age
int age = (int) (Math.random() * 70);
Person p = new Person("Person number " + i, age);
myCollection.add(p);
}
java.util.Iterator it = myCollection.iterator();
while (it.hasNext()) {
Object o = it.next();
System.out.println(o);
}
System.out.println("The collection has " + myCollection.size()
+ " elements");
}
}
- Modify the main code so that the line creating a new Person now reads:
Person p = new Person("Person number " + i, (age - 50));
What happens when you run the program now?
- Modify the "Person" code above so that the first constructor throws an
"IllegalArgumentException" if the age parameter supplied is less than zero.
What do you expect will happen when you try to compile the program now?
- (If you've not already done so) add an exception handler to the program
to deal with any "IllegalArgumentException"s that occur during main
- Change the code so that instead of throwing an "IllegalArgumentException",
it throws an "Exception". What do you expect to happen when you compile the
program now?
- Fix the program so that it builds and runs when the type of exception
thrown is an "Exception"
- Create a new class called a "SillyAgeException". Change your Person class
so that it throws a SillyAgeException for minus ages
- Add methods to your SillyAgeException so that it keeps track of what the
silly age was. In the Person code, catch any SillyAgeExceptions, and display
a message reporting what the silly age was.
- Add code to the "compareTo" method so that it throws a
"ClassCastException" if it is called with an argument that is not a
Person.
- Use a "finally" clause in the main method so that it displays a message
"the program is now finishing"
- Modify the "main" method so that instead of creating 20 Person objects, it
reads from the command line an argument containing the number of objects to
create, e.g.
$ java Person 25
- Make the main method throw an exception if the user asks for more than
100, or less than 5 Person objects.
- When do you expect the "finishing" message to be displayed if
- The program finishes normally
- The program throws an exception because the user asked for 1000
objects?
Try it and see
Back to index page