ENIC4023 - Networked Embedded System Development Exceptions Lab Overview of Java Exceptions Java provides a standard mechanism for dealing with errors: exception handling. This is a means to deal with unusual conditions that can arise during execution and provides for dealing with these conditions in a consistent way. Java has a class called Throwable, from which two other major classes, Error and Exception, inherit. Normally Throwable is not used directly but you do have to have a class that is a subclass of Throwable to throw an error or an exception. All Throwable objects contain information on the exception stack for the executing thread at the time the error or exception object was created. Normally you won't write code to deal with Error or any subclass of it; an Error stems from events like device failures or out-of-memory conditions. Throwable Error Exception RunTimeException Other Exceptions There are many subclasses of Exception. Of these RunTimeExceptions are not normally dealt with in your code. Such exceptions probably represent a program bug, eg if your code tries to access an array element that does not exist, such as the 6th element of a 5-element array object, this would cause an ArrayIndexOutOfBoundsException. Another example that would generate a RunTimeException is trying to use an object before it is instantiated. RunTimeExceptions represent problems in your code, you should not try to catch them, but rather resolve the problems in your code. There are many Exception subclasses that do not represent RunTimExceptions, but are generated by other conditions in a program. Java requires that you deal with these, often these are exceptions that a developer has decided that their code needed to throw under given circumstances. Such an exception might not represent a bug or logic problem, but rather it might merely be an anomalous condition that you might want to try to deal with in your code. Even if you do not want to deal with the condition, the Java compiler won't let you not do something about a thrown Exception. Java has a rule: its the "handle or declare rule". At compile time, the compiler verifies that all checked exceptions are handled or declared in your code, at runtime the Java Virtual Machine implements your choice. Thus you either need to deal with a potential Exception, or tell Java to pass the Exception up the call stack to your method's caller. Java will continue up the call stack looking for some method that will deal with the exception. If it runs out of methods, the Exception is handled by the Java runtime by interrupting execution and displaying the exception on System.err. Handling Exceptions The mechanism Java has for dealing with Exceptions is the try-catch-finally construct. The basic syntax is: try { //put code that might throw //an Exception inside a try block } catch(<SomeExceptionClass> excpObjRef) { //name a specific exception class and object reference //put code here to deal with the //Exception type if it is thrown } finally { //a finally block is optional //if present it is always executed, use it to //do something that needs to be done no matter what } Where a method that you call may throw an Exception (or subclass of Exception), you call it inside a try block (or clause). The best way to know if a method can throw an Exception is to check the API documentation. If it can, it will say: someMethod(<parameters>) throws SomeExceptionClass, someOther..., ... If the signature of the method or constructor says throws someException, it falls under the "handle or declare" rule. If the exception is to be handled, the method or constructor call should be inside a try block. One or more catch blocks should contain the code to handle the exception(s) and followed optionally with a finally block. For one try block you can have essentially any number of catch blocks. Java processes catch blocks by examining the type of Exception class listed in the catch block parameter list. The first catch block whose Exception type matches, or is a superclass of the type of Exception thrown from the try block is executed. Example - Using try ... catch ... finally The Integer class wraps a value of the primitive type int in an object. An object of type Integer contains a single field whose type is int. In addition, this class provides several methods for converting an int to a String and a String to an int, as well as other constants and methods useful when dealing with an int. See the Java API documentation for details. The Integer class has a constructor as shown: public Integer(String s) throws NumberFormatException Thus if the String is of numeric integer format the Integer object will be created, otherwise a NumberFormatException will be thrown. The following application uses this to determine if a command line argument is an integer or not: public class ExDem1 { public static void main(String[] arg) { try { Integer num = new Integer(arg[0]); System.out.println(num.intValue() + " is an integer"); } catch (NumberFormatException e) { System.out.println(arg[0] + " is not an integer"); } finally { System.out.println("No matter what, have a nice day!"); } } } In the try block a new Integer object is created with the value of the command line argument. If this is successful a message is output to say the value is an integer. If an exception occurs during the creation of the Integer object the remainder of the try block is not executed; instead the appropriate catch clause is. Here the only exception that can be thrown is the NumberFormatException, so there is a catch block for it. If more than one exception could have been thrown there could be multiple catch clauses to handle each, or a catch block for a superclass of the exception classes could be used to handle them all. The finally block is always executed no matter whether an exception occurred at all, so the "have a nice day" message will always be output. Try running the above application without a command line argument. An ArrayIndexOutOfBoundsException occurs. As there is no catch clause for this exception it is handled by the java run-time environment in a way that would not be friendly to users of this application. A way of stopping this problem occurring was shown in the Console Applications lab. This involved checking the size of the arg array before accessing it. An alternative here would be to catch the exception; the small addition to the above code to do this is left as an exercise. Example - Throwing an Exception As well as catching exceptions, your code can throw them as well. Throwing an exception is done with the throw keyword. This exception must either be handled or declared in the signature of the method within which it can occur. Consider extending the above example to throw and catch an exception if the number entered is odd. public class ExDem2 { public static void main(String[] arg) { try { Integer num = new Integer(arg[0]); if ((num.intValue() % 2) != 0) throw new ArithmeticException(); System.out.println("number is even"); } catch (NumberFormatException e) { System.out.println("only integers allowed"); } catch (ArithmeticException e) { System.out.println("number is odd"); } finally { System.out.println("No matter what, have a nice day!"); } } } Example - Creating Your Own Exception Class You can also create your own exception subclass by extending the Exception class. Exception subclasses need to provide some common elements. There needs to be a String to store the reason for the exception. Also, there needs to be a constructor for the exception, which accepts a String to put into the reason data member. The getMessage() method may optionally be coded to return the reason for the exception together with other desired information; alternatively the inherited version of getMessage() may be used. The following shows an OddException class to handle an exception when a number is odd (instead of using the more general ArithmeticException): public class OddException extends Exception { String message = ""; public OddException(String reason) { message = reason; } public String getMessage() { return message; } } The application code that uses it is: public class ExDem3 { public static void main(String[] arg) { try { Integer num = new Integer(arg[0]); if ((num.intValue() % 2) != 0) throw new OddException(arg[0]); System.out.println("number is even"); } catch (NumberFormatException e) { System.out.println("NumberFormatException: " + e.getMessage()); System.out.println("only integers allowed"); } catch (OddException e) { System.out.println("OddException: " + e.getMessage()); System.out.println("number is odd"); } finally { System.out.println("No matter what, have a nice day!"); } } } Example - A Queue Class Revisited The Queue class example in the OOPs lab 1 had no way of indicating when the queue was empty or full, hence anomalous conditions could occur when trying to add to a full queue or trying to remove from an empty queue. Having the Queue class throw an exception is one way of handling these situations (though not the only way). The Queue class from the OOPs lab 1 is modified as shown: public class Queue { private private private private private private final int QSIZE = 5; String[] store = new String[QSIZE]; int inLocation = 0; int outLocation = 0; boolean empty = true; boolean full = false; public void enter(String name) throws QueueException { if (full == true) { throw new QueueException("queue full"); } else { inLocation++; inLocation %= QSIZE; empty = false; store[inLocation] = name; if (inLocation == outLocation) { full = true; } } } public String leave() throws QueueException { if (empty == true) { throw new QueueException("queue empty"); } else { outLocation++; outLocation %= QSIZE; full = false; if (inLocation == outLocation) { empty = true; } return store[outLocation]; } } } Two private attributes, each of type boolean are added to store the status of the queue. Where the queue is neither empty or full, both flags will be false. If as a result of an item being entered on the queue, the queue becomes full (as indicated by the in and out indice attributes for the array being equal), the full attribute is set to true. If as a result of an item being removed from the queue, the queue becomes empty (as also indicated by the in and out indice attributes for the array being equal), the empty attribute is set to true. The situation where the queue is full and the enter method is called can now be dealt with; here by throwing a QueueException. Similarly for the situation where the queue is empty and the leave method is called. The QueueException is a programmer defined exception class as shown: public class QueueException extends Exception { String message = "queue error"; public QueueException(String reason) { message = reason; } public String getMessage() { return message; } } When the exception is thrown, a new object of the QueueException class is created. This has a string to store the reason for the exception and a constructor that initialises the string. An application that demonstrates the use of the new Queue class follows: public class QueueDemo { public static void main(String[] arg) { Queue bank = new Queue(); try { bank.enter("Wally"); System.out.println("Wally into bank"); bank.enter("Florence"); System.out.println("Florence into bank"); bank.enter("Snorky"); System.out.println("Snorky into bank"); bank.enter("Dilbert"); System.out.println("Dilbert into bank"); bank.enter("Ermitrude"); System.out.println("Ermitrude into bank"); bank.enter("Musky"); System.out.println("Musky into bank"); } catch (QueueException qe) { System.out.println(qe.getMessage()); } try { System.out.println(bank.leave() + " out of System.out.println(bank.leave() + " out of System.out.println(bank.leave() + " out of System.out.println(bank.leave() + " out of System.out.println(bank.leave() + " out of System.out.println(bank.leave() + " out of } catch (QueueException qe) { System.out.println(qe.getMessage()); } } bank"); bank"); bank"); bank"); bank"); bank"); } A Queue object is created but this has a maximum size of 5 items. An attempt is made to put 6 items into the queue. The first 5 will go on OK but then an exception will be thrown by the Queue object. A method that can potentially throw an exception must be called from within a try block; the corresponding catch block here will just output the exception message to the screen. Similarly, it is then tried to remove 6 items from a (full) queue of 5, and again an exception will be thrown by the Queue object. Check out the above example by running it for yourself by .... (1) (2) (3) (4) (5) (6) (7) (8) Create a new subdirectory to store the files in. Use a text editor to enter the above three classes in separate files (Queue.java, QueueException.java, QueueDemo.java). Open a command prompt and navigate to your subdirectory. Compile the QueueException class with: javac QueueException.java Compile the Queue class with: javac Queue.java Compile the QueueDemo class with: javac QueueDemo.java Run the whole application with: java QueueDemo Experiment with the code!