The goal of this exercise is to practise:
- Reading and interpreting stack traces
- Handling exceptions
- Refactoring with guard clauses
- Creating custom exceptions
This project is organized into three modules that can be compiled and tested independently:
- stack-trace-debugging: A runnable application that throws a deliberate, uncaught exception so you can practise reading stack traces
- exception-handling: Practical exception handling exercises
- custom-exceptions: Custom exception creation and file processing exercises
To run all tests across both modules:
./mvnw clean testTo test a specific module:
./mvnw clean test -pl exception-handling
./mvnw clean test -pl custom-exceptionsBefore writing any exception handling code, it's important to be able to read a stack trace and trace an error back to its source.
The stack-trace-debugging module contains a small, pre-written application that deliberately throws an uncaught exception. There are no tests for this module. Your task is to run the application, interpret the output, and fix the line that causes the exception.
Run the application from the root directory of this repo:
./mvnw -q -pl stack-trace-debugging compile exec:execWhen it runs, the application will fail and print a stack trace. Read it carefully and answer the following for yourself:
- What type of exception was thrown?
- Reading from the top, which method, source file, and line number actually threw the exception?
- Following the trace downwards, what was the chain of method calls (across the different source files) that led there, starting from
main? - Is the line that threw the exception the same one that caused it?
Once you've located the line that causes the exception, fix it so that the application runs to completion and prints the average order value instead of failing. Re-run the command above to confirm the program no longer crashes.
💾 When you've completed this task, remember to commit your work with a descriptive message
Create a class called ExceptionExercises in the exception-handling/src/main/java/com/cbfacademy directory with the following methods that demonstrate proper exception handling techniques. Each method should handle specific exceptions gracefully and return appropriate values.
Using the Java Exception Handling documentation as a guide, implement the methods described below. In each method, replace throw new RuntimeException("Not implemented") with your code.
Create a method that performs division while handling potential arithmetic exceptions:
public Integer divide(Integer numerator, Integer denominator) {
// TODO: Implement this method to divide the numerator by the denominator
// - if either parameter is null, return null
// - catch the ArithmeticException thrown when the denominator is 0 and return null
// - return the result of the division
throw new RuntimeException("Not implemented");
}Create a method that safely accesses list elements:
public Integer getListElement(List<Integer> list, int index) {
// TODO: Implement this method to return the element at the specified index
// - return the element at the given index
// - if the index is out of bounds, catch IndexOutOfBoundsException and return -1
// - if the list is null, return -1
throw new RuntimeException("Not implemented");
}Create a method that safely converts strings to integers:
public Integer convertToInteger(String numberString) {
// TODO: Implement this method to convert a string to an integer
// - return the integer value of the string
// - if the string cannot be converted, catch NumberFormatException and return 0
// - if the string is null, return null
throw new RuntimeException("Not implemented");
}Create a method that safely processes strings:
public String getStringLength(String input) {
// TODO: Implement this method to return a formatted string with the length
// - return "Length: [length]" where [length] is the length of the input string
// - if the input is null, catch NullPointerException and return "Length: 0"
throw new RuntimeException("Not implemented");
}Create a method that demonstrates handling multiple types of exceptions:
public String processData(List<String> data, int index) {
// TODO: Implement this method to process list data
// - get the string at the specified index from the list
// - convert that string to an integer
// - return "Processed: [integer_value]"
// - if index is out of bounds, return "Index out of bounds"
// - if string conversion fails, return "Invalid number format"
// - if the list is null, return "List is null"
throw new RuntimeException("Not implemented");
}Example Usage:
ExceptionExercises exercises = new ExceptionExercises();
// Safe division
Integer result1 = exercises.divide(10, 2); // Returns 5
Integer result2 = exercises.divide(10, 0); // Returns null (handles ArithmeticException)
// Safe list access
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Integer element1 = exercises.getListElement(numbers, 2); // Returns 3
Integer element2 = exercises.getListElement(numbers, 10); // Returns -1 (handles IndexOutOfBoundsException)
// Safe string conversion
Integer num1 = exercises.convertToInteger("123"); // Returns 123
Integer num2 = exercises.convertToInteger("abc"); // Returns 0 (handles NumberFormatException)To verify that your code works as expected, run the provided unit tests.
In your terminal, ensure that you are in the root directory of this repo, then run the following command:
./mvnw clean test -pl exception-handling💾 When you've completed this task, remember to commit your work with a descriptive message
Once your ExceptionExercises methods pass their tests, you'll refactor them to rely on guard clauses rather than wrapping everything in try/catch.
A guard clause validates a condition up front and returns early, keeping the "happy path" of the method un-nested and easier to read. Rather than catching an exception that you can anticipate, you check for the problematic condition first and handle it immediately.
Revisit the same ExceptionExercises class in the exception-handling/src/main/java/com/cbfacademy directory and rewrite your solutions so that predictable failures are handled with explicit early-return checks instead of try/catch where practical (e.g. divide, getListElement and processData can guard against null, out-of-bounds indices and division by zero directly).
Your refactored code must still satisfy the existing tests. In your terminal, ensure that you are in the root directory of this repo, then run the following command:
./mvnw clean test -pl exception-handlingYour refactor is correct when all tests still pass.
💾 When you've completed this task, remember to commit your work with a descriptive message
Create a custom (checked) exception class called FilenameException in the custom-exceptions/src/main/java/com/cbfacademy directory.
Create a class called FileExtension in the same directory with the following methods:
boolean check(String filename)Map<String, int> map(List<String> filenames)
The check method should:
- return
truewhen the file extension is.java - return
falsewhen the file extension is not.java - throw a
FilenameExceptionwhen the file name isnullor an empty string.
The map method should:
- check each provided file's extension and map the returned value as 1 if true or 0 if false
- map
-1when an exception occurs
Example
For the following list of file names: Arrays.asList("App.java", "App.txt", null, "App.md"), the map method should return a map with the following entries:
{"App.java", 1},
{"App.txt", 0},
{null, -1},
{"App.md", 0}To verify that your code works as expected, run the provided unit tests.
In your terminal, ensure that you are in the root directory of this repo, then run the following command:
./mvnw clean test -pl custom-exceptionsOr to only run a specific test, use the -Dtest=[test name] flag, e.g.:
./mvnw clean test -pl custom-exceptions -Dtest=FileExtensionTestYour implementation is correct when all tests pass.
💾 When you've completed this task, remember to commit your work with a descriptive message
If you want to experiment with the provided application in the custom-exceptions App.java file, you can run the following command from the terminal:
./mvnw -q -pl custom-exceptions clean compile exec:java -Dexec.mainClass=com.cbfacademy.App