A crash you can catch, instead of a return code you can forget
C had no real way to force you to check for failure. Java's exceptions make failure impossible to silently ignore — the program stops unless you explicitly decide how to handle it.
After this lesson
You should be able to
- Explain how C's -1 return code convention differs from Java's exception handling.
- Describe Java's exception hierarchy at a high level: Throwable, Exception, Error.
- Explain the termination model Java uses, and contrast it with a resumptive model.
The C way: a convention nobody enforces
In CS105ES, a function signalled failure by returning a special value: fopen returns NULL, an array search returns -1. Nothing in the language forces the caller to check that value. Forget the check, and the program keeps running with garbage data, often crashing much later, far from the real cause.
This is the exact bug class Java's exceptions were built to prevent. A method that cannot complete normally throws an exception object instead of returning a sentinel value, and Java's own rules make certain kinds of exceptions impossible to silently skip past.
Throwable, Exception, and Error
Every throwable object in Java descends from Throwable, which splits into two branches: Exception, for problems a program can reasonably recover from, and Error, for serious problems like running out of memory that a program usually cannot meaningfully recover from. Almost everything you write and catch will be an Exception or one of its subclasses.
Exception itself splits further: checked exceptions, which the compiler forces you to acknowledge with try-catch or throws, and unchecked exceptions (RuntimeException and its subclasses), which the compiler does not force you to handle, usually because they represent programmer mistakes like dividing by zero rather than expected external failures.
Throwable
├── Exception
│ ├── IOException // checked — compiler forces handling
│ └── RuntimeException // unchecked
│ ├── ArithmeticException
│ └── ArrayIndexOutOfBoundsException
└── Error // serious, usually not caughtTermination, not resumption
Java uses a termination model: once an exception is thrown, the method that threw it is abandoned immediately — control never returns to the exact line that failed, even if the exception is caught. This differs from a resumptive model, where a handler could theoretically fix the problem and let execution continue from the point of failure. Java deliberately chose termination because it is simpler to reason about: once something has gone wrong, you cannot pretend the interrupted operation partially succeeded.
This is why, after a catch block runs, execution continues after the try-catch, not back inside the method that threw. Design your recovery code around that fact: whatever partial work the failing method had done is gone, and your catch block must handle the failure from scratch, not attempt to patch a half-finished operation.
Try it yourself
Recall a CS105ES program that checked scanf's return value or a function's -1 sentinel for failure. Write one sentence explaining what would happen in that program if the check were accidentally skipped.
Need a hint?
Think about what value the program would use next — the sentinel value itself, treated as if it were real data.
Check the worked solution
Skipping a -1 check means the program treats -1 as if it were a legitimate result — for example, using it as an array index, or printing it as a real score — silently producing wrong output instead of stopping. This exact silent-failure risk is what Java's checked exceptions are designed to make structurally harder to skip past.
Quick check
Why does Java use a termination model rather than a resumptive model for exceptions?
Why this lesson exists
Syllabus mapping
Concepts of exception handling, benefits of exception handling, Termination or resumptive models, exception hierarchy.
Maps to course outcome CO1.