SEN2001 · Week 12 · Exception Handling and Event Handling

Exception handling and event handling

The concept of exceptions and design questions, binding to a handler and propagation, the try/catch/finally flow in Java, checked/unchecked exceptions, a C++ and Ada comparison; event-driven programming, the listener model and the JavaScript event loop.

exceptionexception handlerraisethrowcatchpropagationcontinuationresumptionterminationfinally clausechecked exceptionunchecked exceptionthrows clausebuilt-in exceptionuser-defined exceptioneventevent handlerlistenerevent-driven programmingevent loopcallback

This week two constructs “outside the normal flow”: exceptions (what happens when something goes wrong?) and events (what runs when something happens?). The common idea in both: there is a handler that runs not in the order you wrote it but triggered by an occurrence. On exams, tracing the try/catch/finally flow and language design comparisons come up most.

Golden rules:

  1. When an exception is thrown, the rest of the try block is skipped.
  2. Catches are tried from top to bottom, the first match wins.
  3. finally runs in every case, even with a return.
  4. If there is no local handler, the exception propagates to the caller.
  5. In Java a checked exception is either caught or declared.

What is an exception?

An exception is an unusual event detected by hardware or software that requires special processing: division by zero, array bounds violation, file not found. The code that handles an exception is called an exception handler. The occurrence of an exception is called raise (throw in Java).

In a language without exception support, how is an error reported? A function returns a special value (an error code) or writes a status to a parameter. The problem: you need to check after every call, the code becomes unreadable, and forgetting to check is easy. Language support separates this: normal code in one place, error handling in another.

Design questions

QuestionJava’s answer
How are handlers specified, what is their scope?catch clauses attached to a try block
How is an exception bound to a handler?The exception’s class matches the class of the catch parameter (or a subclass)
If there is no local handler?It propagates to the caller (propagation); if none at all, the thread ends
Where does control go when the handler finishes?To the statement after the try-catch (termination)
Is there termination code (cleanup)?finally
Are there built-in exceptions?Yes: ArithmeticException, NullPointerException …
Can the user define exceptions?Yes: a class derived from Exception

Continuation: where does control go after the handler finishes? The resumption model returns to where the exception occurred. The termination model does not return; it continues after the construct containing the handler. Java, C++, Ada, C#: termination.

The flow in Java

try {
    System.out.print("1 ");
    int[] x = new int[2];
    x[5] = 1;                    // exception: the rest is skipped
    System.out.print("2 ");
} catch (ArrayIndexOutOfBoundsException e) {
    System.out.print("3 ");
} finally {
    System.out.print("4 ");
}
System.out.println("5");
1 3 4 5

finally and return

static int f() { try { return 1; } finally { System.out.print("fin "); } }
static int g() { try { return 1; } finally { return 2; } }
static int h() { int x = 10; try { return x; } finally { x = 20; } }
fin 1
2
10

finally runs after the return in try but before the method returns. In g, the return of finally overrides. In h, the return value was saved at the moment of return x; changing x afterward does not affect it.

Propagation

static void c(int n) { System.out.print("c" + n + " ");
    if (n == 0) throw new IllegalStateException("zero"); }
static void b(int n) {
    try { c(n); System.out.print("b-after "); }
    catch (IllegalArgumentException e) { System.out.print("b-catch "); }
    finally { System.out.print("b-fin "); }
}
static void a(int n) {
    try { b(n); }
    catch (RuntimeException e) { System.out.print("a-catch:" + e.getMessage() + " "); }
}
// a(1); a(0);
c1 b-after b-fin 
c0 b-fin a-catch:zero 

Tracing recipe: where was the exception born? Is there a matching catch in that function? If not, run the finally blocks and go up to the next caller, and ask the same question.

Class hierarchy: checked and unchecked

Throwable
├── Error                  (JVM errors: OutOfMemoryError)      unchecked
└── Exception                                                   checked
    ├── IOException → FileNotFoundException                     checked
    └── RuntimeException                                        unchecked
        ├── ArithmeticException, NullPointerException
        ├── ArrayIndexOutOfBoundsException, ClassCastException
        └── IllegalArgumentException → NumberFormatException

For checked exceptions the compiler requires one of two options: catch it inside the method, or declare it in the header with throws. The unchecked ones (subclasses of RuntimeException and Error) can occur anywhere, so no declaration is required.

static void read() { throw new java.io.IOException("disk"); }
error: unreported exception IOException; must be caught or declared to be thrown

The catch order must be from specific to general; otherwise the catch below can never be reached and the compiler gives an error:

catch (IOException e) { } catch (FileNotFoundException e) { }
error: exception FileNotFoundException has already been caught

User-defined exception

static class InsufficientFunds extends Exception { InsufficientFunds(String m) { super(m); } }
static void withdraw(int bal, int amt) throws InsufficientFunds {
    if (amt > bal) throw new InsufficientFunds("need " + (amt - bal));
}
try { withdraw(50, 80); } catch (InsufficientFunds e) { System.out.println("caught " + e.getMessage()); }
caught need 30

Rethrowing from inside a handler

try {
    try { throw new RuntimeException("inner"); }
    catch (RuntimeException e) { System.out.print("handle "); throw new IllegalArgumentException("outer"); }
    finally { System.out.print("fin "); }
} catch (IllegalArgumentException e) { System.out.println("rethrown " + e.getMessage()); }
handle fin rethrown outer

Language comparison

JavaC++Ada
What is thrown?Objects of Throwable subclassesAny type (int, string, object)Names declared as exception
Handlercatch (Type e)catch (Type e), catch (...) catches everythingAt the end of a block exception when E1 => ...
Cleanupfinally, try-with-resourcesno finally; destructors (RAII)
Declarationthrows (required for checked)noexcept (the old throw() list)
Built-inManyFew (standard library)Constraint_Error, Program_Error, Storage_Error, Tasking_Error

In Ada, if an exception is not handled in a block, it propagates to the unit containing the block; if it is in a subprogram, to the caller. In C++, when an exception is thrown, as the stack unwinds the destructors of local objects are called, and resources are freed this way.

Event handling

An event is a notification that something happened: a button click, a key press, a timer expiring, data arriving from the network. An event handler is the code that runs in response to the event. The difference from an exception: events are ordinary, expected things, they are the actual job of the program. GUIs and web pages work event-driven: the program waits for events, and when an event arrives, it calls the relevant handler.

The listener model (Java)

The event source keeps a list of the objects listening to it. When the event occurs it calls the handler method of all of them. In Swing, button.addActionListener(listener) and actionPerformed(ActionEvent e).

interface Listener { void on(String e); }
static class Button {
    List<Listener> ls = new ArrayList<>();
    void add(Listener l) { ls.add(l); }
    void click() { for (Listener l : ls) l.on("click"); }
}
Button btn = new Button(); StringBuilder log = new StringBuilder();
btn.add(e -> log.append("A:" + e + " "));
btn.add(e -> log.append("B "));
btn.click(); btn.click();
System.out.println(log.toString().trim());
A:click B A:click B

In C# the same idea is built into the language with delegates and the event keyword: button.Click += handler;.

The JavaScript event loop

The browser and Node.js run an event loop on a single thread. First the synchronous code finishes; then microtasks (promise callbacks); then the tasks in the event queue (timers, clicks).

const out = [];
setTimeout(() => out.push("timeout"), 0);
Promise.resolve().then(() => out.push("promise"));
out.push("sync1");
queueMicrotask(() => out.push("micro"));
out.push("sync2");
setTimeout(() => console.log(out.join(" ")), 10);
sync1 sync2 promise micro timeout

Node’s EventEmitter is the same as the listener model: handlers run in registration order, synchronously; one added with once removes itself after the first event.

first 1 | once 1 | third | first 2 | third

Common mistakes

WrongCorrect
The rest of the try runs after an exceptionIt is skipped
After the handler finishes it returns to the point of the exceptionTermination: to after the try-catch
finally runs only on an exceptionIn every case
An assignment in finally changes the return valueThe saved value does not change; a return in finally overrides
A general catch can be written firstThe specific one first; otherwise a compile error
throws is needed for every exceptionOnly checked exceptions
C++ has finallyIt does not; destructors
setTimeout(…, 0) runs immediatelyAfter the synchronous code and microtasks

Test yourself

Exam-style questions. Solve them on paper first, then reveal the answer.

Question 1easy

What is printed in Java?

try {
    System.out.print("1 ");
    int[] x = new int[2];
    x[5] = 1;
    System.out.print("2 ");
} catch (ArrayIndexOutOfBoundsException e) {
    System.out.print("3 ");
} finally {
    System.out.print("4 ");
}
System.out.println("5");
Show answer
1 3 4 5

x[5] throws an exception; the rest of the try block ("2") is skipped. The matching catch runs: 3. finally runs in every case: 4. Since the exception was caught, the program continues after the try-catch: 5. Java uses the termination model: control does not return to the point of the exception.

Question 2hard

What do these two methods return, and what does f print?

static int f() { try { return 1; } finally { System.out.print("fin "); } }
static int g() { try { return 1; } finally { return 2; } }
Show answer
f prints fin and returns 1; g returns 2

finally runs after the return in try but before the method actually returns. In f, finally only prints, and the return value stays 1. In g, the return in finally overrides the return of try: 2. Writing return in finally is a bad habit for this reason (it also swallows exceptions).

Question 3hard

What does this method return?

static int h() {
    int x = 10;
    try { return x; }
    finally { x = 20; }
}
Show answer
10

The expression return x evaluates the value (10) at that moment and saves it. finally makes x 20 but the saved return value does not change. Because the int is saved by value, the change is not visible; if an object were returned and finally changed the inside of the object, the change would be visible.

Question 4medium

What is printed by a(1) and by a(0)?

static void c(int n) { System.out.print("c" + n + " ");
    if (n == 0) throw new IllegalStateException("zero"); }
static void b(int n) {
    try { c(n); System.out.print("b-after "); }
    catch (IllegalArgumentException e) { System.out.print("b-catch "); }
    finally { System.out.print("b-fin "); }
}
static void a(int n) {
    try { b(n); }
    catch (RuntimeException e) { System.out.print("a-catch:" + e.getMessage() + " "); }
}
Show answer
a(1): c1 b-after b-fin ; a(0): c0 b-fin a-catch:zero

a(1): no exception, normal flow, finally still runs. a(0): c throws IllegalStateException. There is no handler in c → it propagates to b. b's catch expects IllegalArgumentException, which does not match; but finally runs. The exception propagates to a, where it matches RuntimeException (a superclass).

Question 5medium

Does this Java code compile? Why?

try { throw new java.io.FileNotFoundException(); }
catch (java.io.IOException e) { }
catch (java.io.FileNotFoundException e) { }
Show answer
No: FileNotFoundException has already been caught

Handlers are tried from top to bottom and the first match is chosen. IOException is the superclass of FileNotFoundException; the second catch can never be reached (unreachable). Java makes this a compile error. The correct order: from specific to general.

Question 6medium

Does this compile? What are the two ways to fix it?

static void read() { throw new java.io.IOException("disk"); }
Show answer
No; catch it inside read, or declare throws IOException in the header

IOException is a checked exception: the Java compiler requires it either to be caught or to be declared in the method's throws list ("unreported exception IOException; must be caught or declared to be thrown"). Error and the subclasses of RuntimeException (NullPointerException, ArithmeticException) are unchecked, no declaration is needed.

Question 7medium

What is printed in Java?

int count = 0;
for (String s : new String[]{"12", "x", "7", ""}) {
    try { count += Integer.parseInt(s); }
    catch (NumberFormatException e) { count += 100; }
}
System.out.println(count);
Show answer
219

"12" → 12. "x" → NumberFormatException → +100. "7" → +7. The empty string is not a number either → +100. 12 + 100 + 7 + 100 = 219. Because the try-catch is inside the loop, the exception affects only that iteration, and the loop continues.

Question 8hard

What is printed in Java?

try {
    try { throw new RuntimeException("inner"); }
    catch (RuntimeException e) {
        System.out.print("handle ");
        throw new IllegalArgumentException("outer");
    }
    finally { System.out.print("fin "); }
} catch (IllegalArgumentException e) {
    System.out.println("rethrown " + e.getMessage());
}
Show answer
handle fin rethrown outer

The inner catch catches the exception, then throws a new one. Before going to the outer catch, the finally of the inner try runs. An exception thrown from inside a handler goes to the outer block, not to the other catches of the same try.

Question 9medium

Name the language: (a) exceptions are objects of classes descended from Throwable, with checked and unchecked kinds; (b) any type can be thrown, catch (...) catches everything, no finally; (c) handlers are attached to blocks with exception when ... => and predefined exceptions include Constraint_Error.

Show answer
(a) Java (b) C++ (c) Ada

Java: exceptions are objects, Throwable → Error / Exception → RuntimeException. C++: int, string, any class can be thrown; catch (...) is the general handler; cleanup is done with destructors (RAII). Ada: an exception is caught in handlers at the end of a block or subprogram; Constraint_Error is raised on array bounds or range violations.

Question 10hard

In which order are the strings logged by this JavaScript code?

setTimeout(() => out.push("timeout"), 0);
Promise.resolve().then(() => out.push("promise"));
out.push("sync1");
queueMicrotask(() => out.push("micro"));
out.push("sync2");
Show answer
sync1 sync2 promise micro timeout

Event-driven execution: first the synchronous code runs to the end. Then the microtask queue (promise, queueMicrotask) in the order added. Last, the timer callback in the event queue (even with setTimeout 0). Handlers are called by the event loop, not within the program's flow.

Question 11easy

A Java Button keeps a list of listeners and calls each with the event name on click(). Two listeners are registered: one appends "A:" + e + " ", the other appends "B ". What does the log contain after two clicks?

Show answer
A:click B A:click B

The listener (observer) model: the event source (Button) keeps a list of the objects listening to it, and when the event occurs it calls the handler method of each in registration order. In Java Swing, addActionListener and actionPerformed are the same structure. The source does not know what the listeners do.

Question 12medium

With Node's EventEmitter, on('save', first), once('save', once), on('save', third) are registered, then emit('save', 1) and emit('save', 2) are called. What is the sequence of handler calls?

Show answer
first 1, once 1, third, first 2, third

Handlers run in registration order. A handler added with once removes itself after the first event and is not called on the second emit. emit runs synchronously: the handlers finish before emit returns.

Verification: All Java outputs were run with the JDK (java verify/SEN2001/W12.java); the catch order and undeclared checked exception examples were attempted to compile with javac and gave the expected compile errors (verify/SEN2001/w12b/). The JavaScript event loop order and the EventEmitter example were run with node (verify/SEN2001/12.mjs). The C++ and Ada information is conceptual; their compilers are not installed.

Review card

exception: an unusual situation (hardware or software), a handler is the code that handles it without exception support: return an error code / report through a parameter (check on every call, unreadable) design: binding to a handler, propagation, continuation (termination vs resumption), finally, built-in? Java/C++/Ada: TERMINATION model (no return to the point of the exception) propagation: if there is no local handler it propagates to the caller; if none at all the program ends catches top to bottom, FIRST match. write specific to general (otherwise compile error) finally ALWAYS runs. return in finally → overrides the return of try return x; finally { x = 20; } → returns 10 (the value was saved beforehand) checked (Exception, IOException): catch or throws. unchecked: RuntimeException, Error C++: any type can be thrown, catch(...), no finally. Ada: exception when ... => event: notification of a user/system action. the handler is called when the event occurs listener: the source keeps a list, calls in registration order on the event (Swing addActionListener) JS: synchronous → microtask (promise) → macrotask (setTimeout)