Enums, Exceptions, and Robust Error Handling
`try`, `catch`, `finally`, `throw`, and `throws`
You are viewing a free preview lesson.
Lesson Overview
আগের lesson-এ আমরা শিখেছি:
- Exception কী
- Checked এবং unchecked exception
- Exception propagation
- Stack trace
throwএবংthrows-এর basic difference
এখন আমরা exception handle করার actual syntax শিখব।
Java provides:
try
catch
finally
throw
throws
এই lesson-এ আমরা শিখব:
tryblock- Specific exception catch করা
- Multiple
catchblocks - Catch ordering
- Multi-catch
finally- Exception rethrow করা
- Exception wrapping
- Original cause preserve করা
- Return statement এবং
finally - Resource cleanup
- Try-with-resources-এর introduction
- কখন exception locally handle করা উচিত
- কখন caller-এর কাছে propagate করা উচিত
Learning Objectives
এই lesson শেষে আপনি পারবেন:
try-catchblock লিখতে- Specific exception handle করতে
- Multiple failure types আলাদাভাবে process করতে
- Correct catch order maintain করতে
finallyblock ব্যবহার করতে- Exception rethrow এবং wrap করতে
- Original cause preserve করতে
- Checked exception catch বা declare করতে
- Try-with-resources দিয়ে resource safely close করতে
- Swallowed এবং misleading handling avoid করতে
The try Block
যে code exception throw করতে পারে, তাকে try block-এর মধ্যে রাখা যায়।
try {
int result =
10 / 0;
System.out.println(
result
);
}
try একা ব্যবহার করা যায় না।
এর সঙ্গে অন্তত একটি থাকতে হবে:
catch
finally
বা দুটোই।
Basic try-catch
try {
int result =
10 / 0;
System.out.println(
result
);
} catch (
ArithmeticException exception
) {
System.out.println(
"Division by zero is not allowed."
);
}
Output:
Division by zero is not allowed.
Exception catch হওয়ায় application uncontrolledভাবে terminate করেনি।
How Control Flow Works
try {
System.out.println(
"Before"
);
int result =
10 / 0;
System.out.println(
"After"
);
} catch (
ArithmeticException exception
) {
System.out.println(
"Handled"
);
}
System.out.println(
"Finished"
);
Output:
Before
Handled
Finished
This does not print:
After
Exception throw হওয়ার সঙ্গে সঙ্গে remaining try statements skip হয়।
Catch Parameter
catch (
ArithmeticException exception
)
এখানে:
ArithmeticException → Exception type
exception → Local variable
Catch block-এর মধ্যে ব্যবহার করা যায়:
exception.getMessage()
exception.getCause()
exception.printStackTrace()
Reading the Exception Message
try {
Integer.parseInt(
"Java"
);
} catch (
NumberFormatException exception
) {
System.out.println(
exception.getMessage()
);
}
Possible output:
For input string: "Java"
Message useful হতে পারে, কিন্তু user-facing response হিসেবে raw library message সবসময় appropriate নয়।
Catch Only Matching Types
try {
int result =
10 / 0;
} catch (
NumberFormatException exception
) {
System.out.println(
"Invalid number."
);
}
ArithmeticException এবং NumberFormatException different types।
এই catch failure handle করবে না।
Exception caller-এর দিকে propagate করবে।
Catching a Parent Exception
Because:
ArithmeticException extends RuntimeException
This catches it:
try {
int result =
10 / 0;
} catch (
RuntimeException exception
) {
System.out.println(
"Runtime failure."
);
}
And this also catches it:
catch (
Exception exception
)
But broader catches lose precision।
Prefer the most specific type the current layer can meaningfully handle।
Specific Catch Is Better
Weak:
try {
parseCoursePrice(
input
);
} catch (
Exception exception
) {
System.out.println(
"Invalid price."
);
}
This may incorrectly label:
NullPointerException- Database failure
- Programming bug
- Number parsing failure
as the same problem।
Better:
try {
parseCoursePrice(
input
);
} catch (
NumberFormatException exception
) {
System.out.println(
"Price must be a valid number."
);
}
Multiple catch Blocks
A try block different exception types throw করতে পারে।
try {
String value =
values.get(
index
);
int number =
Integer.parseInt(
value
);
System.out.println(
number
);
} catch (
IndexOutOfBoundsException exception
) {
System.out.println(
"The selected index does not exist."
);
} catch (
NumberFormatException exception
) {
System.out.println(
"The selected value is not a number."
);
}
Only the first matching catch executes।
Catch Ordering
Child exception catch parent exception-এর আগে থাকতে হবে।
Correct:
try {
// Operation
} catch (
NumberFormatException exception
) {
// Specific handling
} catch (
RuntimeException exception
) {
// Broader fallback
}
Incorrect:
try {
// Operation
} catch (
RuntimeException exception
) {
// Catches NumberFormatException too
} catch (
NumberFormatException exception
) {
// Unreachable
}
Compiler rejects the second catch because parent catch already handles every NumberFormatException।
Most Specific to Most General
Recommended order:
Specific child exception
↓
Broader parent exception
↓
General fallback, only if necessary
Example:
catch (
FileNotFoundException exception
) {
} catch (
IOException exception
) {
} catch (
Exception exception
) {
}
Multi-Catch
If multiple exception types need exactly the same handling:
try {
// Operation
} catch (
NumberFormatException
| IndexOutOfBoundsException exception
) {
System.out.println(
"Input could not be processed."
);
}
This avoids duplicate catch blocks।
Multi-Catch Restrictions
Types in one multi-catch cannot have a parent-child relationship।
Invalid:
catch (
RuntimeException
| NumberFormatException exception
) {
}
Because NumberFormatException is already a RuntimeException।
When Not to Combine Catches
Do not use multi-catch when failures need different responses।
Weak:
catch (
NumberFormatException
| IOException exception
) {
System.out.println(
"Invalid input."
);
}
An IOException may not mean invalid input।
Separate them when meaning or recovery differs।
The finally Block
finally runs after try and catch flow completes, whether exception occurred or not।
try {
System.out.println(
"Trying"
);
} finally {
System.out.println(
"Cleanup"
);
}
Output:
Trying
Cleanup
try-catch-finally
try {
int result =
10 / 0;
} catch (
ArithmeticException exception
) {
System.out.println(
"Handled"
);
} finally {
System.out.println(
"Always runs"
);
}
Output:
Handled
Always runs
finally Without a Matching Catch
try {
int result =
10 / 0;
} finally {
System.out.println(
"Cleanup"
);
}
finally runs।
Then ArithmeticException continues propagating।
Output starts with:
Cleanup
but application still fails unless an outer caller catches the exception।
Typical Uses of finally
Historically, finally commonly handled cleanup:
Close file
Close stream
Release lock
Restore temporary state
Release connection
Example:
Resource resource =
null;
try {
resource =
openResource();
useResource(
resource
);
} finally {
if (resource != null) {
resource.close();
}
}
Modern Java usually prefers try-with-resources for closeable resources।
finally and Return
public static int calculate() {
try {
return 10;
} finally {
System.out.println(
"Finally"
);
}
}
The method prints:
Finally
then returns:
10
finally executes before method completion।
Never Return from finally
Dangerous:
public static int calculate() {
try {
return 10;
} finally {
return 20;
}
}
Result:
20
The finally return overrides the try return।
Even worse, it can suppress an exception।
finally Can Hide an Exception
public static int calculate() {
try {
throw new IllegalStateException(
"Failure"
);
} finally {
return 20;
}
}
The exception is suppressed by the return in finally।
Caller receives:
20
This is extremely misleading।
Rule:
Do not return, throw a different unrelated exception, or use control-flow statements from
finallyunless there is a very deliberate reason.
Throwing an Exception
public static void validateLearnerId(
long learnerId
) {
if (learnerId <= 0) {
throw new IllegalArgumentException(
"Learner ID must be positive."
);
}
}
throw works with an exception object।
Invalid:
throw "Invalid";
Only Throwable subclasses can be thrown।
Declaring with throws
public static String readCourseContent(
Path path
) throws IOException {
return Files.readString(
path
);
}
The method passes responsibility to the caller।
Caller must:
- Catch
IOException - Or declare it again
Catching a Checked Exception
public static String loadContent(
Path path
) {
try {
return Files.readString(
path
);
} catch (
IOException exception
) {
return "";
}
}
This compiles, but returning empty text may hide the difference between:
Actual empty file
Failed file read
Handling must preserve meaningful failure semantics।
Declaring a Checked Exception
public static String loadContent(
Path path
) throws IOException {
return Files.readString(
path
);
}
Now caller chooses how to handle the infrastructure failure।
This is often better when current method cannot recover meaningfully।
Catch or Propagate?
Catch an exception when the current layer can:
- Recover
- Provide a fallback that is actually valid
- Retry safely
- Translate to a more meaningful abstraction
- Convert it into a clear boundary response
- Add context and preserve the cause
Propagate when the current layer cannot make a useful decision।
Bad Recovery
try {
return Files.readString(
path
);
} catch (
IOException exception
) {
return "";
}
This may pretend success।
If empty content is not a valid fallback, propagate or wrap the failure instead।
Valid Fallback
Suppose optional description file is genuinely optional:
public static String loadOptionalDescription(
Path path
) {
try {
return Files.readString(
path
);
} catch (
NoSuchFileException exception
) {
return "No description available.";
} catch (
IOException exception
) {
throw new IllegalStateException(
"Could not read course description.",
exception
);
}
}
Missing optional file has a valid fallback।
Other I/O failures are not silently hidden।
Rethrowing the Same Exception
try {
processEnrollment();
} catch (
IllegalStateException exception
) {
System.out.println(
"Enrollment processing failed."
);
throw exception;
}
This logs or adds local action, then propagates the same exception।
Be careful not to log the same exception at every layer, creating duplicate noisy logs।
Rethrowing Preserves the Original Stack Trace
throw exception;
propagates the existing exception object।
Original failure location remains visible।
Avoid throw new Exception(exception.getMessage())
Weak:
catch (
IOException exception
) {
throw new IllegalStateException(
exception.getMessage()
);
}
Problems:
- Original exception type lost
- Original cause chain lost
- Message may lack domain context
Better:
catch (
IOException exception
) {
throw new IllegalStateException(
"Could not load course content.",
exception
);
}
Exception Wrapping
Wrapping means catching a lower-level exception and throwing a higher-level exception।
try {
return Files.readString(
path
);
} catch (
IOException exception
) {
throw new IllegalStateException(
"Could not load course content from storage.",
exception
);
}
Now caller sees an application-relevant failure while cause remains preserved।
Why Wrap an Exception?
Lower-level API may expose implementation details:
IOException
SQLException
SocketTimeoutException
Higher-level service may want to expose:
CourseContentLoadException
CourseRepositoryException
PaymentProcessingException
This reduces coupling between abstraction layers।
Custom exceptions come in a later lesson।
Add Useful Context When Wrapping
Weak:
throw new IllegalStateException(
"Failed.",
exception
);
Better:
throw new IllegalStateException(
"Could not load content for course "
+ courseCode
+ ".",
exception
);
Include safe context that helps diagnosis।
Do not include secrets or private data।
Catching and Throwing the Same Type Unnecessarily
Redundant:
try {
saveCourse();
} catch (
IOException exception
) {
throw exception;
}
This adds no value।
Simply allow propagation:
saveCourse();
Catch only when you add meaningful handling, translation, cleanup, or context।
Exception Variable Naming
Prefer:
exception
or a descriptive name:
ioException
validationException
Avoid meaningless:
e
in teaching and application code where readability matters।
Short names may be common, but clear names improve comprehension।
Catch Block Scope
The exception variable exists only inside its catch block।
try {
// Operation
} catch (
IOException exception
) {
System.out.println(
exception.getMessage()
);
}
// exception is not accessible here
Nested try-catch
Possible:
try {
try {
loadCourse();
} catch (
IOException exception
) {
recoverFromMissingFile();
}
publishCourse();
} catch (
IllegalStateException exception
) {
reportPublicationFailure();
}
But deeply nested handling becomes difficult to follow।
Prefer extracting focused methods when possible।
One try Block Should Be Focused
Weak:
try {
parseInput();
loadDatabase();
calculatePrice();
sendEmail();
writeFile();
} catch (
Exception exception
) {
System.out.println(
"Operation failed."
);
}
It is unclear which operation failed and what can be recovered।
Better to create meaningful boundaries and catch specific failures near the layer that understands them।
Avoid Huge try Blocks
Large try blocks:
- Catch unrelated exceptions
- Make failure origin less obvious
- Encourage broad catch
- Mix recovery policies
Keep protected operation focused।
Try-With-Resources
Resources such as:
File readers
Streams
Database resources
Network streams
often need closing।
Java provides:
try (
Resource resource =
openResource()
) {
useResource(
resource
);
}
The resource is closed automatically।
A File Reading Example
public static String readFirstLine(
Path path
) throws IOException {
try (
BufferedReader reader =
Files.newBufferedReader(
path
)
) {
return reader.readLine();
}
}
When the block completes:
- Normally
- Through return
- Through exception
reader.close() is called automatically।
Why Try-With-Resources Is Better
Manual cleanup:
BufferedReader reader =
null;
try {
reader =
Files.newBufferedReader(
path
);
return reader.readLine();
} finally {
if (reader != null) {
reader.close();
}
}
Problems:
- More code
- Cleanup itself may throw
- Null checks required
- Easy to forget
- Multiple resources become complicated
Try-with-resources is clearer and safer।
AutoCloseable
A resource can be used in try-with-resources if it implements:
AutoCloseable
or a compatible subtype such as:
Closeable
Many Java I/O and database resource types implement this contract।
Multiple Resources
try (
InputStream input =
Files.newInputStream(
sourcePath
);
OutputStream output =
Files.newOutputStream(
targetPath
)
) {
input.transferTo(
output
);
}
Resources close automatically in reverse declaration order।
output closes first
input closes second
Suppressed Exceptions
Suppose:
- Main operation throws
- Resource closing also throws
Java preserves the main exception and attaches close failure as a suppressed exception।
It can be inspected using:
exception.getSuppressed()
This is another reason try-with-resources is safer than manual cleanup।
finally Is Still Useful
Try-with-resources replaces many resource-closing uses, but finally can still be useful for:
- Releasing a lock
- Restoring temporary state
- Clearing thread-local context
- Ending timing measurement
- Cleanup not modeled by
AutoCloseable
Use it carefully and avoid hiding the original failure।
Handling Exceptions at a Boundary
Suppose application service throws:
IllegalArgumentException
An API boundary may catch it and convert it to:
HTTP 400 Bad Request
A missing resource may become:
HTTP 404 Not Found
An infrastructure failure may become:
HTTP 500 Internal Server Error
Domain and infrastructure code should not directly return HTTP responses unless that is their responsibility।
Boundaries translate failures into transport-specific results।
Do Not Expose the Same Message Everywhere
Internal exception:
Database connection refused at db-internal:5432
External response:
Unable to process the request right now.
Detailed cause belongs in secure logs।
User-facing response should be safe and actionable।
Complete Example: Loading Course Content
CourseContentLoader.java
package io.liveklass.content;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public final class CourseContentLoader {
public String load(
Path path
) {
if (path == null) {
throw new IllegalArgumentException(
"Content path is required."
);
}
try {
return Files.readString(
path
);
} catch (
IOException exception
) {
throw new IllegalStateException(
"Could not load course content from "
+ path.getFileName()
+ ".",
exception
);
}
}
}
Calling the Loader
public class Main {
public static void main(
String[] args
) {
CourseContentLoader loader =
new CourseContentLoader();
try {
String content =
loader.load(
Path.of(
"course.md"
)
);
System.out.println(
content
);
} catch (
IllegalArgumentException exception
) {
System.out.println(
"Invalid request: "
+ exception.getMessage()
);
} catch (
IllegalStateException exception
) {
System.out.println(
"Content could not be loaded."
);
exception.printStackTrace();
} finally {
System.out.println(
"Load attempt finished."
);
}
}
}
Possible Output for a Missing File
Content could not be loaded.
Load attempt finished.
Stack trace also shows:
IllegalStateException:
Could not load course content from course.md.
Caused by:
NoSuchFileException
Design Review
Why Validate path Before try?
Null path is a caller contract violation।
It is not a file I/O failure।
Keeping validation outside the try prevents unrelated handling from mixing।
Why Catch IOException?
The loader translates storage-level failures into an application-level failure।
Why Preserve the Cause?
The caller sees meaningful context while developers retain the original file failure।
Why Does finally Print Last?
It runs after normal or exceptional handling completes।
Complete Example: Parsing Learner Input
public static int parseCompletedLessons(
String value
) {
if (
value == null
|| value.isBlank()
) {
throw new IllegalArgumentException(
"Completed lesson count is required."
);
}
try {
int count =
Integer.parseInt(
value.strip()
);
if (count < 0) {
throw new IllegalArgumentException(
"Completed lesson count cannot be negative."
);
}
return count;
} catch (
NumberFormatException exception
) {
throw new IllegalArgumentException(
"Completed lesson count must be a valid integer.",
exception
);
}
}
Why Wrap NumberFormatException?
Caller should understand the domain field:
Completed lesson count
not only Java parsing terminology।
The cause remains available for debugging।
Be Careful Catching Your Own Exception
In the previous method, IllegalArgumentException thrown for negative count is not caught by:
catch (
NumberFormatException exception
)
This is good।
If catch were:
catch (
IllegalArgumentException exception
)
it would also catch the manually thrown negative-count failure and potentially wrap it incorrectly।
Catch scope and type both matter।
Keep Only Parsing Inside the Parsing Catch
Even clearer:
public static int parseCompletedLessons(
String value
) {
if (
value == null
|| value.isBlank()
) {
throw new IllegalArgumentException(
"Completed lesson count is required."
);
}
int count;
try {
count =
Integer.parseInt(
value.strip()
);
} catch (
NumberFormatException exception
) {
throw new IllegalArgumentException(
"Completed lesson count must be a valid integer.",
exception
);
}
if (count < 0) {
throw new IllegalArgumentException(
"Completed lesson count cannot be negative."
);
}
return count;
}
The try block now protects only the operation that can produce the parsing failure।
Common Mistakes
Catching Too Broadly
catch (
Exception exception
)
may hide unrelated problems।
Wrong Catch Order
Parent exception before child makes child catch unreachable।
Empty Catch Block
Swallows the failure।
Returning Success After Catching a Failure
Creates contradictory behavior।
Returning from finally
Can override return values and suppress exceptions।
Throwing a New Exception Without the Cause
Loses the original failure chain।
Catching Only to Rethrow Without Adding Value
Creates unnecessary code।
Using a False Fallback
Returning empty content after a failed read may pretend success।
Huge try Blocks
Mix unrelated failure sources।
Logging the Same Exception at Every Layer
Creates duplicate noisy logs।
Manually Closing Resources When Try-With-Resources Fits
Adds avoidable complexity।
Catching Throwable
Can hide serious JVM errors।
Practice Exercises
Exercise 1: Parse Price
Create:
static long parsePrice(
String value
)
Requirements:
- Blank input →
IllegalArgumentException - Non-numeric input → wrap
NumberFormatException - Negative number →
IllegalArgumentException - Preserve parsing cause
Exercise 2: Multiple Catch Blocks
Write a method that:
- Reads an element from a list by index
- Parses it as integer
- Handles invalid index and invalid number separately
Exercise 3: Catch Ordering
Explain why this fails:
catch (
RuntimeException exception
) {
} catch (
NumberFormatException exception
) {
}
Correct the order।
Exercise 4: Finally Behavior
Predict whether finally runs when:
trycompletes normallytryreturnstrythrows and catch handlestrythrows and no catch matches
Exercise 5: Preserve Cause
Catch:
IOException
and wrap it in:
IllegalStateException
with message:
Could not load lesson content.
Exercise 6: Try-With-Resources
Read the first line of a file using:
BufferedReader
and try-with-resources।
Exercise 7: Find the Bug
Explain what is wrong:
public boolean save() {
try {
repository.save();
return true;
} catch (
Exception exception
) {
exception.printStackTrace();
}
return true;
}
Exercise 8: Result or Exception
Choose behavior for:
- Optional course search found nothing
- Invalid negative course ID
- File cannot be read
- Duplicate enrollment is expected
- Impossible internal state
- User enters non-numeric price
Explain whether to return a result, throw, catch, or translate।
Predict the Result
Question 1
try {
System.out.println(
"A"
);
int result =
10 / 0;
System.out.println(
"B"
);
} catch (
ArithmeticException exception
) {
System.out.println(
"C"
);
} finally {
System.out.println(
"D"
);
}
System.out.println(
"E"
);
Answer
A
C
D
E
Question 2
public static int value() {
try {
return 10;
} finally {
System.out.println(
"Finished"
);
}
}
Answer
Prints:
Finished
Returns:
10
Question 3
public static int value() {
try {
return 10;
} finally {
return 20;
}
}
Answer
Returns:
20
The finally return overrides the try return।
This design should be avoided।
Question 4
try {
Integer.parseInt(
"Java"
);
} catch (
RuntimeException exception
) {
System.out.println(
"Runtime"
);
} catch (
NumberFormatException exception
) {
System.out.println(
"Number"
);
}
Answer
The code does not compile।
NumberFormatException catch is unreachable because the parent RuntimeException catch appears first।
Question 5
try {
throw new IllegalStateException(
"Failure"
);
} finally {
System.out.println(
"Cleanup"
);
}
Answer
Prints:
Cleanup
Then the IllegalStateException propagates।
Knowledge Check
Question 1
What does a try block contain?
Question 2
What happens to remaining try statements after an exception?
Question 3
How is a matching catch selected?
Question 4
Why must child exception catches appear before parent catches?
Question 5
What is multi-catch?
Question 6
When does finally run?
Question 7
Why should you avoid returning from finally?
Question 8
What is the difference between throw and throws?
Question 9
When should an exception be caught locally?
Question 10
When should it be propagated?
Question 11
What is exception wrapping?
Question 12
Why preserve the original cause?
Question 13
What is try-with-resources?
Question 14
Which interface makes a resource compatible with try-with-resources?
Question 15
Why should a try block remain focused?
Knowledge Check Answers
Answer 1
Code whose exceptional outcome needs handling, cleanup, or translation।
Answer 2
They are skipped and control moves to a matching catch or outward propagation।
Answer 3
Java chooses the first compatible catch block in declaration order।
Answer 4
A parent catch already accepts every child exception, making later child catches unreachable।
Answer 5
One catch block handling multiple unrelated exception types with the same behavior।
Answer 6
After try and matching catch flow, including normal completion, return, or exception propagation in most ordinary cases।
Answer 7
It can override normal return values and suppress exceptions।
Answer 8
throw signals a specific exception object; throws declares possible propagation in a method signature।
Answer 9
When the current layer can recover, provide a valid fallback, translate, or add meaningful context।
Answer 10
When the current layer cannot make a useful handling decision।
Answer 11
Catching a lower-level exception and throwing a higher-level exception, usually with the original cause।
Answer 12
It preserves the technical origin and full debugging chain।
Answer 13
A Java construct that automatically closes compatible resources after the block।
Answer 14
AutoCloseable।
Answer 15
Focused blocks avoid mixing unrelated failure sources and enable precise handling।
Lesson Summary
এই lesson-এ আমরা শিখেছি:
tryexception-prone operationকে handling boundary দেয়- Matching
catchexceptional flow handle করে - Remaining
trystatements exception-এর পরে execute হয় না - Specific exception catches broad catches-এর চেয়ে safer
- Multiple catches different failures differently handle করতে পারে
- Child catches parent catches-এর আগে থাকতে হয়
- Multi-catch same handling share করা unrelated exceptions combine করে
finallynormal এবং exceptional flow-এর পরে cleanup করতে পারেfinally-তে return exception এবং result hide করতে পারেthrowspecific exception signal করেthrowspossible propagation declare করে- Checked exceptions catch বা declare করতে হয়
- Catch only when current layer can meaningfully handle
- Invalid fallback failure hide করতে পারে
- Rethrowing same exception propagation continue করে
- Wrapping lower-level failure higher-level context provide করে
- Original cause constructor-এ pass করা উচিত
- Catching only to rethrow without adding value unnecessary
- Large
tryblocks unrelated failures mix করে - Try-with-resources closeable resources automatically close করে
AutoCloseableresources try-with-resources support করে- Multiple resources reverse order-এ close হয়
- Suppressed exceptions cleanup failure preserve করতে পারে
- Boundary layers internal exceptionsকে user-safe responses-এ translate করতে পারে
- Handling means correct observable outcome, শুধু stack trace print করা নয়
Next Lesson
পরবর্তী lesson:
Checked vs Unchecked Exceptions
আমরা শিখব:
- Compiler enforcement
RuntimeException- Checked exception contracts
- API design trade-offs
- Recoverability myths
- Validation failures
- Infrastructure failures
- Wrapping checked exceptions
- When custom exceptions should be checked or unchecked
- Avoiding exception declaration pollution