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.
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:
- When an exception is thrown, the rest of the try block is skipped.
- Catches are tried from top to bottom, the first match wins.
- finally runs in every case, even with a return.
- If there is no local handler, the exception propagates to the caller.
- 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
| Question | Java’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
| Java | C++ | Ada | |
|---|---|---|---|
| What is thrown? | Objects of Throwable subclasses | Any type (int, string, object) | Names declared as exception |
| Handler | catch (Type e) | catch (Type e), catch (...) catches everything | At the end of a block exception when E1 => ... |
| Cleanup | finally, try-with-resources | no finally; destructors (RAII) | |
| Declaration | throws (required for checked) | noexcept (the old throw() list) | |
| Built-in | Many | Few (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
| Wrong | Correct |
|---|---|
| The rest of the try runs after an exception | It is skipped |
| After the handler finishes it returns to the point of the exception | Termination: to after the try-catch |
| finally runs only on an exception | In every case |
| An assignment in finally changes the return value | The saved value does not change; a return in finally overrides |
| A general catch can be written first | The specific one first; otherwise a compile error |
| throws is needed for every exception | Only checked exceptions |
| C++ has finally | It does not; destructors |
| setTimeout(…, 0) runs immediately | After the synchronous code and microtasks |
Test yourself
Exam-style questions. Solve them on paper first, then reveal the answer.
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
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.
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
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).
What does this method return?
static int h() {
int x = 10;
try { return x; }
finally { x = 20; }
}Show answer
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.
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): 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).
Does this Java code compile? Why?
try { throw new java.io.FileNotFoundException(); }
catch (java.io.IOException e) { }
catch (java.io.FileNotFoundException e) { }Show answer
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.
Does this compile? What are the two ways to fix it?
static void read() { throw new java.io.IOException("disk"); }Show answer
throws IOException in the headerIOException 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.
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
"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.
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
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.
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
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.
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
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.
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
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.
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
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.