Project: Robust Input Handling
Project: Robust Input Handling
You have spent the last nine lessons learning every corner of Java exception handling — from the basics of try/catch all the way to custom exception classes and try-with-resources. This final lesson brings everything together in one realistic mini-project: a console application that keeps asking the user for input until they provide something valid, and signals bad input by throwing a custom exception.
Real applications rarely crash on bad input — they tell the user what went wrong and ask again. That is the pattern you will build here.
What We Are Building
A small command-line program that reads an integer between 1 and 100 from the user. If the user types a non-number, or a number outside that range, the program:
- Throws an
InvalidInputException(a custom checked exception you define). - Catches it, prints a friendly message, and loops back to prompt again.
- Exits the loop only when valid input is received.
Step 1 — Define the Custom Exception
From Lesson 7 you know that a custom exception is simply a class that extends Exception (checked) or RuntimeException (unchecked). Here we use a checked exception because the caller must handle bad input — it is an expected part of the flow.
Step 2 — A Validator Method That Throws
Separate the validation logic into its own method. It accepts a raw String, tries to parse it, checks the range, and either returns a clean int or throws InvalidInputException. The throws declaration makes the contract explicit.
NumberFormatException is caught and immediately re-thrown as InvalidInputException. The original low-level exception is absorbed, and a meaningful domain-level exception is raised in its place. The user sees "not a whole number", not a raw stack trace.
Step 3 — The Input Loop
The main loop runs indefinitely (while (true)) and breaks only when valid input arrives. On every iteration it prompts, reads a line, calls the validator, and either stores the result and breaks, or catches the exception and prints the error before looping.
Sample Run
Here is what the program looks like when a user makes two mistakes before succeeding:
Why This Design Works
- Single responsibility:
parseAndValidatehandles all validation;mainhandles the loop and user interaction. Each piece does one job. - Checked exception forces handling: Because
InvalidInputExceptionextendsException, the compiler forces every caller to either catch it or declarethrows. You cannot accidentally ignore bad input. - No silent failures: The loop never exits until valid input is produced. There is no
return -1sentinel value to forget to check. - Informative error messages: The exception carries both a human-readable message and the raw input, so the catch block can give the user specific feedback.
Extending the Project (Ideas)
Now that the pattern is solid, try these small extensions on your own:
- Add a maximum retry limit (e.g., three attempts), then throw a different exception if the limit is exceeded.
- Validate that input is not blank before trying to parse it, and give a specific message for empty input.
- Wrap the
Scannerin atry-with-resourcesblock (Lesson 8) so it is always closed — even if an unexpected exception escapes the loop. - Move the validator into a utility class and write a unit test that checks every error branch.
Putting It All Together
This project touched almost every concept from the tutorial: defining a custom exception class (Lesson 7), using throws in a method signature (Lesson 5), catching and re-throwing from a low-level exception (Lesson 6), and structuring a retry loop that gives the user meaningful feedback. Exception handling is not just about stopping crashes — it is about building programs that communicate clearly and recover gracefully. That is the skill you now have.