UPI Study › Courses › Data Structures and Algorithms › Free lessons › Lesson 4.3 Free with a free account
Anticipating and Recovering from Runtime Errors Gracefully
Data Structures and Algorithms · Chapter 4: Abstract Types, Exceptions, and Nested ClassesSection 4.3 · lesson 13 of 8711 min · free with a free account
Free preview
Create a free account to keep watching
It takes a minute and costs nothing. Your free account unlocks all of Chapter 1 and 3 more lessons of Data Structures and Algorithms.
Lesson complete
Earn the 3 college credits for Data Structures and Algorithms
Enrolling adds a quiz after each lesson, the graded work and the official transcript. Recommended for credit by NCCRS.
Next lesson: Grouping Helper Classes Inside Other Classes →← Turning Data Definitions into Working Java CodeNext lesson: Grouping Helper Classes Inside Other Classes →
In this lesson
Handle, throw and design Java exceptions so failures are clear.
- Use try, catch and finally to recover from exceptions and clean up
- Distinguish throw from throws and checked from unchecked exceptions
- Create custom exceptions for data structure failures such as an empty or full stack
- Validate public method arguments so bad input fails fast
Free study notes
Get this lesson's notes by email
The key points and 4 practice questions with answers, in your inbox. One email, free.
One email. Unsubscribe in one click.
Transcript
Everything said in the video, section by section (1,513 words). Read along, or search it.
Opening Question
When a Program Cannot Continue
Even a well-designed program meets moments it can't complete. A user types letters where a number belongs. A file is missing. Or code asks a stack for its top item while the stack is empty. Java reports each of these by creating an exception: an object that describes what went wrong, and where. So what should your program do next?
Unhandled Exception
Reading an Uncaught Stack Trace
Here, main asks readQuantity to turn the text abc into a number. Nothing handles the failure. Run it. The program stops, and prints a stack trace: the list of method calls that were active at that moment. The first line names the exception, and why. Below it, the calls run from the deepest, inside parseInt, back up through readQuantity, to main. When a method throws, Java leaves it at once, and travels back up that chain of callers, looking for the nearest one that will handle it. Here, nobody did.
Key Terms
Try, Catch, Finally, Throw and Throws
Handling it gracefully means deciding in advance what to do instead. Five keywords carry almost all of Java's error handling. try marks code that might fail, and should be watched. catch handles one kind of exception raised inside the try block. finally holds cleanup code that runs whether anything failed or not. And two look alike, but do opposite jobs. throw raises an exception, right now. throws, in a method's heading, warns callers that it may raise one.
Worked Example
Three Paths Through a Try Statement
Here's a try statement. The lines that can fail go in the try block, the recovery in the catch block, and the closing message in finally. check runs three times. With 4, nothing fails: the try block runs, then finally. With abc, parseInt fails. The rest of the try block is skipped, the catch block runs, and then finally. With 0, parsing works, but 12 divided by 0 throws a different exception, one with no matching catch. finally still runs, and then the exception moves on up. That's why finally is the place for cleanup: it sits on every path.
Best Practices
Habits for Trustworthy Catch Blocks
Three habits make catch blocks trustworthy. First, list specific exceptions before general ones. Java uses the first catch that fits. Put catch Exception above catch NumberFormatException, and the compiler refuses: that exception has already been caught. Second, catch only what you can actually handle. And third, never leave a catch block empty. That hides the failure completely. Here, the general catch quietly sets total to zero, and reports nothing. Any failure at all would vanish without a trace.
Quick Check
Order of Output With Finally
Quick check, with a new program. It prints A, writes to index 2 of a two-element array, and has a line that prints B. There's a catch for an index out of bounds that prints C, a finally that prints D, and a last line that prints E. What does it print, in order? Pause and predict. It prints A, C, D, E. Index 2 is past the end of a two-element array, so the write throws, and the rest of the try block, B included, is skipped. The matching catch prints C, and finally prints D. And because the exception was handled, the program carries on, and prints E.
Raising Exceptions
Refusing an Oversized Withdrawal
Use throw when your own code detects a problem. withdraw refuses an amount larger than the balance, with a message that names the amount. The first call withdraws 40. The second asks for 250 out of 100, and the exception stops the program, message and all.
Declaring Exceptions
Checked IOException and Try With Resources
throws is for a different case. firstLine opens a file and reads its first line. File operations can raise IOException, a checked exception. Write it with no handling, and the compiler refuses twice: each exception must be caught, or declared to be thrown. So declare it. throws IOException in the heading passes the decision to the caller. And try with resources closes the file automatically. Now main catches it, and recovers. With no settings file, the program falls back to its defaults.
Exception Hierarchy
Throwable, Error and Exception
Every exception type belongs to one family tree. At the top is Throwable. Below it, Error covers serious problems in Java itself, such as running out of memory. Programs aren't expected to catch those. Exception covers problems a program can reasonably anticipate. And inside that branch, RuntimeException and everything below it are unchecked. A checked exception must be caught or declared, or the program won't compile. An unchecked one carries no such rule.
Design Choice
Checked Versus Unchecked
Which kind should a failure be? Checked exceptions come from outside conditions, ones the program doesn't control. The answer is to recover, retry, or report. Unchecked ones signal a mistake in the calling code: a bad argument, a null, a bad index. The answer is to fix the code. So, as a rule of thumb, make an exception checked when a careful caller could reasonably recover from it. Forcing callers to wrap avoidable mistakes in try blocks only produces empty catch blocks.
Reference
Built-In Exceptions You Will Meet
You'll meet these built-in ones again and again. NullPointerException: using a reference that holds null. ArrayIndexOutOfBoundsException, past the end of an array. NumberFormatException, text such as abc turned into a number. And ArithmeticException, a whole number divided by zero. IllegalStateException: a method called when the object isn't ready. All of those are unchecked. IOException, a failed file or network operation, is checked.
Quick Check
Which Failure Should Be Checked
Quick check, with three new failures. Only one of them suits a checked exception. A website a program calls doesn't respond. A method is handed an index of minus one. A method is handed a null name. Which should be checked? Pause, and decide. The website. A server going quiet is an outside condition no caller can prevent, and a careful caller can recover: retry, or tell the user. An index of minus one, and a null name, are both mistakes the caller should have avoided. So those are unchecked.
Custom Exceptions
Naming a Data Structure's Failures
A data structure has failures of its own: removing from an empty stack, or adding to a full one. A custom exception gives each its own name. It's a small class that extends an existing exception. Popping an empty stack is the caller's mistake, so this one extends RuntimeException, and is unchecked. It has two constructors: one with a default message, and one that lets the data structure supply a message that fits. FullStructureException is written the same way.
Worked Example
A Bounded Stack That Throws
Now a bounded stack, which keeps its values in an array, uses both. push checks for a full stack first, and pop checks for an empty one. Each throws the matching custom exception, with a message that includes the limit. The demo makes a stack with room for two, and pops it while it's empty. A precise catch block reports the message, and nothing else is caught by accident. Then it pushes three values. The third doesn't fit, and the trace names the problem exactly: FullStructureException, stack is full, capacity 2.
Design Reasoning
Exceptions Versus Special Return Values
Why throw, instead of returning a special value such as minus 1 or null? Because a special value can be ignored, and an exception can't. If pop returned minus 1, minus 1 might be a value that was really pushed. And callers can forget to check. Return null, and the next use fails far from the cause. Print a message and carry on, and the program keeps going with bad data. Name it for the problem, not the place. Include the offending value or limit in the message. And reuse a built-in type when its meaning already fits.
Defensive Coding
Failing Fast at the Door
Defensive coding means assuming callers will sometimes pass bad data, and checking for it before it does harm. The best place is the start of every public method. This average has no checks. Give it 4 and 8, and it's 6. Give it an empty array, and it quietly returns NaN, not a number. That's a wrong answer that spreads through every later calculation. So check at the door. Refuse a null array, and refuse an empty one, with a message that names the problem. Now the bad call fails at once, right where the mistake is. This is called failing fast.
Reference
Standard Checks for Bad Arguments
A few checks cover most cases. A reference that must not be null: Objects dot requireNonNull. A value outside its range: IllegalArgumentException. An index outside the valid positions: IndexOutOfBoundsException. And a call at the wrong moment: IllegalStateException.
Best Practices
Validation Dos and Don'ts
Validate every argument of a public method, but don't re-check the same values in every private helper. Put the bad value in the message, not just the word error. Let failures you can't handle move up to a caller who can. And never use exceptions for normal control flow, such as looping until one occurs.
Key Takeaways
Handling, Throwing and Validating
To sum up. An exception travels up the chain of callers until a matching catch handles it. try watches, catch recovers, and finally always runs. throw raises an exception, and throws declares one. Checked exceptions must be caught or declared. Unchecked ones signal a caller's mistake. Custom exceptions make a data structure's failures precise, and impossible to ignore. And validate arguments early, so bad input fails fast.
Think About It
The Worst Input Your Method Could Get
Something to think about. Pick a method you've written that takes input. What's the worst value it could receive? Would it fail loudly with a clear message, or quietly produce a wrong answer?
About Data Structures and Algorithms at UPI Study
Data Structures and Algorithms (Computer Science 251) is a self-paced online course of 87 video lessons in 17 chapters, from Java and object-oriented design to arrays, linked lists, recursion, stacks and queues, trees, heaps, hash tables, sorting and graphs. It carries a NCCRS credit recommendation for 3 semester credits.
Every lesson transcript is free on pages like this one, and the videos are free with a free UPI Study account. Enrolling adds a practice quiz after each lesson, the graded work and final exam that earn the credit, lifetime access, and an official transcript on completion. UPI Study has helped over 130,000 students with college credits. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.
Enroll now → Course details Download the syllabus All Computer Science courses Every course with PRO →
Compare: Discrete Math vs Data Structures and Algorithms · Intro to Java vs Data Structures and Algorithms
Why take it here
Why students take Data Structures and Algorithms with UPI Study
Where the credit goes
Universities that work with NCCRS
Data Structures and Algorithms carries a NCCRS credit recommendation. These universities work with NCCRS; some are UPI Study's signed partners. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.
Thomas Edison State UniversitySigned partner
University of the PeopleSigned partner
Charter Oak State CollegeSigned partner
Strayer UniversitySigned partner
Columbia Southern UniversitySigned partner
Indiana TechSigned partner
Penn StateWorks with NCCRS
Southern New Hampshire UniversityWorks with NCCRS
SUNYWorks with NCCRS
+many more cooperating universitiesFind my college →Find my college → Not listed? Ask Athena in the corner of this page, or book a 15-minute call.
FAQ
Questions students ask us
About Anticipating and Recovering from Runtime Errors Gracefully
What is the difference between throw and throws in Java?
What is the difference between checked and unchecked exceptions in Java?
When does a finally block run in Java?
Why throw an exception instead of returning -1 or null?
About the Data Structures and Algorithms course
How many college credits is Data Structures and Algorithms?
How much does Data Structures and Algorithms cost and what is included?
Which universities accept UPI Study credits?
How long does Data Structures and Algorithms take?
How are the exams done?
How do my credits reach my college?
Ready for the full course? Earn 3 credits in Data Structures and Algorithms.
87 video lessons, a quiz after each one, and the graded work that earns the credit. Recommended for credit by NCCRS.
3 credits · NCCRSEnroll →