Exceptions
Custom Exceptions
Domain-Specific Errors
Your banking app needs to signal "insufficient funds". Using generic Exception
loses meaning. Custom exceptions like InsufficientFundsException carry the
balance and requested amount - meaningful error information.
Simple custom exception
Create your own exception type.
// Creating Basic Custom Exceptions
public class SimpleCustom {
public static void main(String[] args) {
System.out.println("=== Basic Custom Exceptions ===\n");
// Simple custom exception
System.out.println("--- Simple Custom Exception ---");
try {
validateAge(-5);
} catch (InvalidAgeException e) {
System.out.println("Caught: " + e.getMessage());
}
// Custom exception with valid input
System.out.println("\n--- Valid Input ---");
try {
validateAge(25);
System.out.println("Age 25 is valid!");
} catch (InvalidAgeException e) {
System.out.println("Error: " + e.getMessage());
}
// Another custom exception
System.out.println("\n--- Another Custom Exception ---");
try {
validateUsername("ab");
} catch (InvalidUsernameException e) {
System.out.println("Caught: " + e.getMessage());
}
// Multiple custom exceptions
System.out.println("\n--- Multiple Custom Exceptions ---");
String[] usernames = {"alice123", "x", "bob_smith", ""};
int[] ages = {25, -1, 200, 30};
for (int i = 0; i < usernames.length; i++) {
try {
validateUsername(usernames[i]);
validateAge(ages[i]);
System.out.println("✓ Valid: " + usernames[i] + ", age " + ages[i]);
} catch (InvalidUsernameException e) {
System.out.println("✗ Username error: " + e.getMessage());
} catch (InvalidAgeException e) {
System.out.println("✗ Age error: " + e.getMessage());
}
}
System.out.println("\n=== Key Points ===");
System.out.println("""
1. Extend RuntimeException for unchecked
2. Extend Exception for checked
3. Call super(message) to set message
4. Name should end with 'Exception'
5. Name should describe the error clearly
""");
}
static void validateAge(int age) {
if (age < 0) {
throw new InvalidAgeException("Age cannot be negative: " + age);
}
if (age > 150) {
throw new InvalidAgeException("Age cannot exceed 150: " + age);
}
}
static void validateUsername(String username) {
if (username == null || username.isEmpty()) {
throw new InvalidUsernameException("Username cannot be empty");
}
if (username.length() < 3) {
throw new InvalidUsernameException("Username must be at least 3 characters: " + username);
}
}
}
// Simple custom exception - unchecked
class InvalidAgeException extends RuntimeException {
public InvalidAgeException(String message) {
super(message);
}
}
// Another simple custom exception
class InvalidUsernameException extends RuntimeException {
public InvalidUsernameException(String message) {
super(message);
}
}
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
public static void main(String[] args)
3public class SimpleCustom {4 public static void main(String[] args) {5 System.out.println("=== Basic Custom Exceptions ===\n");67 // Simple custom exception //?simple_demo8 System.out.println("--- Simple Custom Exception ---");output=== Basic Custom Exceptions === --- Simple Custom Exception ---static void validateAge(int age)
pass 1 of 463static void validateAge(int age-5) { //?validate_age_method64 if (age < 0) { //?check_negativeAll 4 passes — pass 1 is the card above pass agemessageeages[i]iusernames[i]username1 -5 Age cannot be negative: -5 InvalidAgeException: Age cannot be negative: -5 — — — — 2 25 — InvalidUsernameException: Username must be at least 3 characters: ab — — — — 3 25 — InvalidUsernameException: Username must be at least 3 characters: x 25 0 alice123 — 4 200 Age cannot exceed 150: 200 InvalidAgeException: Age cannot exceed 150: 200 — — — (empty) if (age < 0)
63static void validateAge(int age) { //?validate_age_method64 if (age-5 < 0) { //?check_negative65 throw new InvalidAgeException("Age cannot be negative: " + age); //?throw_negative66 }public InvalidAgeException(String message)
pass 1 of 283class InvalidAgeException extends RuntimeException { //?invalid_age_def84 public InvalidAgeException(String messageAge cannot be negative: -5) { //?invalid_age_constructor85 super(message); //?super_callcatch (InvalidAgeException e)
11 validateAge(-5); //?call_validate_age12} catch (InvalidAgeException eInvalidAgeException: Age cannot be negative: -5) { //?catch_simple13 System.out.println("Caught: " + e.getMessage()); //?print_simple14}outputCaught: Age cannot be negative: -5System.out.println(" --- Valid Input ---");
16// Custom exception with valid input //?valid_input17System.out.println("\n--- Valid Input ---");output --- Valid Input ---static void validateUsername(String username)
pass 1 of 572static void validateUsername(String usernameab) { //?validate_username_method73 if (username == null || username.isEmpty()) { //?check_emptyAll 5 passes — pass 1 is the card above pass usernameeusernames[i]iages[i]agemessage1 ab InvalidUsernameException: Username must be at least 3 characters: ab — — — — — 2 alice123 — alice123 0 25 — — 3 x InvalidUsernameException: Username must be at least 3 characters: x — — — — — 4 bob_smith InvalidAgeException: Age cannot exceed 150: 200 bob_smith 2 200 200 Age cannot exceed 150: 200 5 (empty) InvalidUsernameException: Username cannot be empty — — — — — public InvalidUsernameException(String message)
pass 1 of 390class InvalidUsernameException extends RuntimeException { //?invalid_username_def91 public InvalidUsernameException(String messageUsername must be at least 3 characters: ab) { //?invalid_username_constructor92 super(message); //?super_call_usernameAll 3 passes — pass 1 is the card above pass messageeageusername1 Username must be at least 3 characters: ab InvalidUsernameException: Username must be at least 3 characters: ab — — 2 Username must be at least 3 characters: x InvalidUsernameException: Username must be at least 3 characters: x 200 (empty) 3 Username cannot be empty InvalidUsernameException: Username cannot be empty — — catch (InvalidUsernameException e)
30 validateUsername("ab"); //?call_validate_username31} catch (InvalidUsernameException eInvalidUsernameException: Username must be at least 3 characters: ab) { //?catch_username32 System.out.println("Caught: " + e.getMessage()); //?print_username33}outputCaught: Username must be at least 3 characters: abString[] usernames = {"alice123", "x", "bob_smith", ""}; //?usernames
35// Multiple custom exceptions //?multiple_custom36System.out.println("\n--- Multiple Custom Exceptions ---");3738String[] usernames = {"alice123", "x", "bob_smith", ""}; //?usernames39int[] ages = {25, -1, 200, 30}; //?agesoutput --- Multiple Custom Exceptions ---for (int i = 0; i < usernames.length; i++)
pass 1 of 441for (int i0 = 0; i < usernames.length4; i++) { //?loop_validate42 try { //?try_bothAll 4 passes — pass 1 is the card above pass ieagemessageusername1 0 — — — — 2 1 InvalidUsernameException: Username must be at least 3 characters: x — — — 3 2 InvalidAgeException: Age cannot exceed 150: 200 200 Age cannot exceed 150: 200 — 4 3 InvalidUsernameException: Username cannot be empty — — (empty) try
pass 1 of 441for (int i = 0; i < usernames.length; i++) { //?loop_validate42 try { //?try_both43 validateUsername(usernames[i]alice123); //?validate_user44 validateAge(ages[i]); //?validate_age_loopAll 4 passes — pass 1 is the card above pass usernames[i]ieagemessageusername1 alice123 0 — — — — 2 x 1 InvalidUsernameException: Username must be at least 3 characters: x — — — 3 bob_smith 2 InvalidAgeException: Age cannot exceed 150: 200 200 Age cannot exceed 150: 200 — 4 (empty) 3 InvalidUsernameException: Username cannot be empty — — (empty) catch (InvalidUsernameException e)
pass 1 of 245 System.out.println("✓ Valid: " + usernames[i] + ", age " + ages[i]); //?print_valid46} catch (InvalidUsernameException eInvalidUsernameException: Username must be at least 3 characters: x) { //?catch_user47 System.out.println("✗ Username error: " + e.getMessage()); //?print_user_error48} catch (InvalidAgeException e) { //?catch_ageoutput✗ Username error: Username must be at least 3 characters: xif (age > 150)
66}67if (age200 > 150) { //?check_max68 throw new InvalidAgeException("Age cannot exceed 150: " + age); //?throw_max69}public InvalidAgeException(String message)
pass 2 of 283class InvalidAgeException extends RuntimeException { //?invalid_age_def84 public InvalidAgeException(String messageAge cannot exceed 150: 200) { //?invalid_age_constructor85 super(message); //?super_callcatch (InvalidAgeException e)
47 System.out.println("✗ Username error: " + e.getMessage()); //?print_user_error48} catch (InvalidAgeException eInvalidAgeException: Age cannot exceed 150: 200) { //?catch_age49 System.out.println("✗ Age error: " + e.getMessage()); //?print_age_error50}output✗ Age error: Age cannot exceed 150: 200if (username == null || username.isEmpty())
72static void validateUsername(String username) { //?validate_username_method73 if (username(empty) == null || username.isEmpty()) { //?check_empty74 throw new InvalidUsernameException("Username cannot be empty"); //?throw_empty75 }catch (InvalidUsernameException e)
pass 2 of 245 System.out.println("✓ Valid: " + usernames[i] + ", age " + ages[i]); //?print_valid46} catch (InvalidUsernameException eInvalidUsernameException: Username cannot be empty) { //?catch_user47 System.out.println("✗ Username error: " + e.getMessage()); //?print_user_error48} catch (InvalidAgeException e) { //?catch_ageoutput✗ Username error: Username cannot be emptySystem.out.println(" === Key Points ===");
53 System.out.println("\n=== Key Points ===");54 System.out.println("""55 1. Extend RuntimeException for unchecked56 2. Extend Exception for checked57 3. Call super(message) to set message58 4. Name should end with 'Exception'59 5. Name should describe the error clearly60 """);61}output === Key Points === 1. Extend RuntimeException for unchecked 2. Extend Exception for checked 3. Call super(message) to set message 4. Name should end with 'Exception' 5. Name should describe the error clearly
Extend RuntimeException (unchecked) or Exception (checked).
Exception with data
Include relevant data in the exception.
// Custom Exceptions with Additional Data
public class ExceptionWithData {
public static void main(String[] args) {
System.out.println("=== Custom Exceptions with Data ===\n");
// Exception with numeric data
System.out.println("--- Exception with Numeric Data ---");
double transferAmount = 150.00;
try {
transferMoney(100.00, transferAmount);
} catch (InsufficientFundsException e) {
System.out.println("Transfer failed!");
System.out.println(" Message: " + e.getMessage());
System.out.println(" Balance: $" + e.getBalance());
System.out.println(" Requested: $" + e.getRequestedAmount());
System.out.println(" Shortfall: $" + e.getShortfall());
}
// Exception with string data
System.out.println("\n--- Exception with String Data ---");
try {
findUser("unknown_user");
} catch (UserNotFoundException e) {
System.out.println("User lookup failed!");
System.out.println(" Message: " + e.getMessage());
System.out.println(" Username: " + e.getUsername());
System.out.println(" Search time: " + e.getSearchTimeMs() + "ms");
}
// Exception with enum data
System.out.println("\n--- Exception with Enum Data ---");
try {
validateInput("", "email");
} catch (ValidationException e) {
System.out.println("Validation failed!");
System.out.println(" Message: " + e.getMessage());
System.out.println(" Field: " + e.getFieldName());
System.out.println(" Error type: " + e.getErrorType());
System.out.println(" Is required error? " + e.isRequiredError());
}
// Using exception data for recovery
System.out.println("\n--- Using Data for Recovery ---");
double balance = 100.00;
double[] amounts = {50.00, 75.00, 30.00};
for (double amount : amounts) {
try {
withdraw(balance, amount);
balance -= amount;
System.out.println("✓ Withdrew $" + amount + ", new balance: $" + balance);
} catch (InsufficientFundsException e) {
System.out.println("✗ Cannot withdraw $" + amount);
System.out.println(" Suggestion: withdraw up to $" + e.getBalance());
}
}
System.out.println("\n=== Key Points ===");
System.out.println("""
1. Add fields to store relevant error data
2. Provide getters to access the data
3. Include data in the message for readability
4. Caller can use data for error recovery
5. Consider computed properties (like shortfall)
""");
}
static void transferMoney(double balance, double amount) {
if (amount > balance) {
throw new InsufficientFundsException(balance, amount);
}
System.out.println("Transfer of $" + amount + " successful");
}
static void withdraw(double balance, double amount) {
if (amount > balance) {
throw new InsufficientFundsException(balance, amount);
}
}
static void findUser(String username) {
// Simulate search with a fixed duration
long searchTime = 50;
// User not found
throw new UserNotFoundException(username, searchTime);
}
static void validateInput(String value, String fieldName) {
if (value == null || value.isBlank()) {
throw new ValidationException(fieldName, ValidationErrorType.REQUIRED);
}
}
}
// Exception with numeric data
class InsufficientFundsException extends RuntimeException {
private final double balance;
private final double requestedAmount;
public InsufficientFundsException(double balance, double requestedAmount) {
super(String.format(
"Insufficient funds: balance=$%.2f, requested=$%.2f",
balance, requestedAmount
));
this.balance = balance;
this.requestedAmount = requestedAmount;
}
public double getBalance() {
return balance;
}
public double getRequestedAmount() {
return requestedAmount;
}
public double getShortfall() {
return requestedAmount - balance;
}
}
// Exception with string data
class UserNotFoundException extends RuntimeException {
private final String username;
private final long searchTimeMs;
public UserNotFoundException(String username, long searchTimeMs) {
super("User not found: " + username);
this.username = username;
this.searchTimeMs = searchTimeMs;
}
public String getUsername() {
return username;
}
public long getSearchTimeMs() {
return searchTimeMs;
}
}
// Validation error type enum
enum ValidationErrorType {
REQUIRED,
INVALID_FORMAT,
TOO_SHORT,
TOO_LONG
}
// Exception with enum data
class ValidationException extends RuntimeException {
private final String fieldName;
private final ValidationErrorType errorType;
public ValidationException(String fieldName, ValidationErrorType errorType) {
super(fieldName + ": " + errorType.name().toLowerCase().replace('_', ' '));
this.fieldName = fieldName;
this.errorType = errorType;
}
public String getFieldName() {
return fieldName;
}
public ValidationErrorType getErrorType() {
return errorType;
}
public boolean isRequiredError() {
return errorType == ValidationErrorType.REQUIRED;
}
}
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
// Custom Exceptions with Additional Data
public class ExceptionWithData {
public static void main(String[] args) {
System.out.println("=== Custom Exceptions with Data ===\n");
// Exception with numeric data
System.out.println("--- Exception with Numeric Data ---");
double transferAmount = 75.00;
try {
transferMoney(100.00, transferAmount);
} catch (InsufficientFundsException e) {
System.out.println("Transfer failed!");
System.out.println(" Message: " + e.getMessage());
System.out.println(" Balance: $" + e.getBalance());
System.out.println(" Requested: $" + e.getRequestedAmount());
System.out.println(" Shortfall: $" + e.getShortfall());
}
// Exception with string data
System.out.println("\n--- Exception with String Data ---");
try {
findUser("unknown_user");
} catch (UserNotFoundException e) {
System.out.println("User lookup failed!");
System.out.println(" Message: " + e.getMessage());
System.out.println(" Username: " + e.getUsername());
System.out.println(" Search time: " + e.getSearchTimeMs() + "ms");
}
// Exception with enum data
System.out.println("\n--- Exception with Enum Data ---");
try {
validateInput("", "email");
} catch (ValidationException e) {
System.out.println("Validation failed!");
System.out.println(" Message: " + e.getMessage());
System.out.println(" Field: " + e.getFieldName());
System.out.println(" Error type: " + e.getErrorType());
System.out.println(" Is required error? " + e.isRequiredError());
}
// Using exception data for recovery
System.out.println("\n--- Using Data for Recovery ---");
double balance = 100.00;
double[] amounts = {50.00, 75.00, 30.00};
for (double amount : amounts) {
try {
withdraw(balance, amount);
balance -= amount;
System.out.println("✓ Withdrew $" + amount + ", new balance: $" + balance);
} catch (InsufficientFundsException e) {
System.out.println("✗ Cannot withdraw $" + amount);
System.out.println(" Suggestion: withdraw up to $" + e.getBalance());
}
}
System.out.println("\n=== Key Points ===");
System.out.println("""
1. Add fields to store relevant error data
2. Provide getters to access the data
3. Include data in the message for readability
4. Caller can use data for error recovery
5. Consider computed properties (like shortfall)
""");
}
static void transferMoney(double balance, double amount) {
if (amount > balance) {
throw new InsufficientFundsException(balance, amount);
}
System.out.println("Transfer of $" + amount + " successful");
}
static void withdraw(double balance, double amount) {
if (amount > balance) {
throw new InsufficientFundsException(balance, amount);
}
}
static void findUser(String username) {
// Simulate search with a fixed duration
long searchTime = 50;
// User not found
throw new UserNotFoundException(username, searchTime);
}
static void validateInput(String value, String fieldName) {
if (value == null || value.isBlank()) {
throw new ValidationException(fieldName, ValidationErrorType.REQUIRED);
}
}
}
// Exception with numeric data
class InsufficientFundsException extends RuntimeException {
private final double balance;
private final double requestedAmount;
public InsufficientFundsException(double balance, double requestedAmount) {
super(String.format(
"Insufficient funds: balance=$%.2f, requested=$%.2f",
balance, requestedAmount
));
this.balance = balance;
this.requestedAmount = requestedAmount;
}
public double getBalance() {
return balance;
}
public double getRequestedAmount() {
return requestedAmount;
}
public double getShortfall() {
return requestedAmount - balance;
}
}
// Exception with string data
class UserNotFoundException extends RuntimeException {
private final String username;
private final long searchTimeMs;
public UserNotFoundException(String username, long searchTimeMs) {
super("User not found: " + username);
this.username = username;
this.searchTimeMs = searchTimeMs;
}
public String getUsername() {
return username;
}
public long getSearchTimeMs() {
return searchTimeMs;
}
}
// Validation error type enum
enum ValidationErrorType {
REQUIRED,
INVALID_FORMAT,
TOO_SHORT,
TOO_LONG
}
// Exception with enum data
class ValidationException extends RuntimeException {
private final String fieldName;
private final ValidationErrorType errorType;
public ValidationException(String fieldName, ValidationErrorType errorType) {
super(fieldName + ": " + errorType.name().toLowerCase().replace('_', ' '));
this.fieldName = fieldName;
this.errorType = errorType;
}
public String getFieldName() {
return fieldName;
}
public ValidationErrorType getErrorType() {
return errorType;
}
public boolean isRequiredError() {
return errorType == ValidationErrorType.REQUIRED;
}
}
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
transferAmount ← 150.0
3public class ExceptionWithData {4 public static void main(String[] args) {5 System.out.println("=== Custom Exceptions with Data ===\n");67 // Exception with numeric data //?numeric_data8 System.out.println("--- Exception with Numeric Data ---");910 double transferAmount→ 150.0 = 150.00; //@transferAmount=150.00, 75.0011 try { //?try_transferoutput=== Custom Exceptions with Data === --- Exception with Numeric Data ---try
10double transferAmount = 150.00; //@transferAmount=150.00, 75.0011try { //?try_transfer12 transferMoney(100.00, transferAmount150.0); //?call_transfer13} catch (InsufficientFundsException e) { //?catch_transferstatic void transferMoney(double balance, double amount)
73static void transferMoney(double balance100.0, double amount150.0) { //?transfer_method74 if (amount > balance) { //?check_fundsif (amount > balance)
73static void transferMoney(double balance, double amount) { //?transfer_method74 if (amount150.0 > balance100.0) { //?check_funds75 throw new InsufficientFundsException(balance, amount); //?throw_insufficient76 }this.balance ← 100.0, this.requestedAmount ← 150.0
pass 1 of 2106public InsufficientFundsException(double balance100.0, double requestedAmount150.0) { //?insufficient_constructor107 super(String.format( //?format_message108 "Insufficient funds: balance=$%.2f, requested=$%.2f",109 balance, requestedAmount110 ));111 this.balance→ 100.0 = balance100.0; //?set_balance112 this.requestedAmount→ 150.0 = requestedAmount150.0; //?set_requested113}catch (InsufficientFundsException e)
12 transferMoney(100.00, transferAmount); //?call_transfer13} catch (InsufficientFundsException eInsufficientFundsException: Insufficient funds: balance=$100.00, requested=$150.00) { //?catch_transfer14 System.out.println("Transfer failed!"); //?print_failed15 System.out.println(" Message: " + e.getMessage()); //?print_message16 System.out.println(" Balance: $" + e.getBalance()); //?print_balance17 System.out.println(" Requested: $" + e.getRequestedAmount()); //?print_requestedoutputTransfer failed! Message: Insufficient funds: balance=$100.00, requested=$150.00public double getBalance()
pass 1 of 2115public double getBalance() { //?get_balance116 return balance100.0;117}System.out.println(" Balance: $" + e.getBalance()); //?print_balance
15System.out.println(" Message: " + e.getMessage()); //?print_message16System.out.println(" Balance: $" + e.getBalance()); //?print_balance17System.out.println(" Requested: $" + e.getRequestedAmount()); //?print_requested18System.out.println(" Shortfall: $" + e.getShortfall()); //?print_shortfalloutput Balance: $100.0public double getRequestedAmount()
119public double getRequestedAmount() { //?get_requested120 return requestedAmount150.0;121}System.out.println(" Requested: $" + e.getRequestedAmount()); //?prin…
16 System.out.println(" Balance: $" + e.getBalance()); //?print_balance17 System.out.println(" Requested: $" + e.getRequestedAmount()); //?print_requested18 System.out.println(" Shortfall: $" + e.getShortfall()); //?print_shortfall19}output Requested: $150.0public double getShortfall()
123public double getShortfall() { //?get_shortfall124 return requestedAmount150.0 - balance100.0; //?calc_shortfall125}System.out.println(" Shortfall: $" + e.getShortfall()); //?print_shor…
17 System.out.println(" Requested: $" + e.getRequestedAmount()); //?print_requested18 System.out.println(" Shortfall: $" + e.getShortfall()); //?print_shortfall19}2021// Exception with string data //?string_data22System.out.println("\n--- Exception with String Data ---");output Shortfall: $50.0 --- Exception with String Data ---searchTime ← 50
86static void findUser(String usernameunknown_user) { //?find_user_method87 // Simulate search with a fixed duration //?simulate_search88 long searchTime→ 50 = 50; //?calc_time8990 // User not found //?not_found91 throw new UserNotFoundException(username, searchTime); //?throw_not_found92}this.username ← unknown_user, this.searchTimeMs ← 50
133public UserNotFoundException(String usernameunknown_user, long searchTimeMs50) { //?user_constructor134 super("User not found: " + username); //?user_message135 this.username→ unknown_user = usernameunknown_user; //?set_username136 this.searchTimeMs→ 50 = searchTimeMs50; //?set_search_time137}catch (UserNotFoundException e)
25 findUser("unknown_user"); //?call_find_user26} catch (UserNotFoundException eUserNotFoundException: User not found: unknown_user) { //?catch_user27 System.out.println("User lookup failed!"); //?print_user_failed28 System.out.println(" Message: " + e.getMessage()); //?print_user_message29 System.out.println(" Username: " + e.getUsername()); //?print_username30 System.out.println(" Search time: " + e.getSearchTimeMs() + "ms"); //?print_search_timeoutputUser lookup failed! Message: User not found: unknown_userpublic String getUsername()
139public String getUsername() { //?get_username140 return usernameunknown_user;141}System.out.println(" Username: " + e.getUsername()); //?print_usernam…
28 System.out.println(" Message: " + e.getMessage()); //?print_user_message29 System.out.println(" Username: " + e.getUsername()); //?print_username30 System.out.println(" Search time: " + e.getSearchTimeMs() + "ms"); //?print_search_time31}output Username: unknown_userpublic long getSearchTimeMs()
143public long getSearchTimeMs() { //?get_search_time144 return searchTimeMs50;145}System.out.println(" Search time: " + e.getSearchTimeMs() + "ms"); //…
29 System.out.println(" Username: " + e.getUsername()); //?print_username30 System.out.println(" Search time: " + e.getSearchTimeMs() + "ms"); //?print_search_time31}3233// Exception with enum data //?enum_data34System.out.println("\n--- Exception with Enum Data ---");output Search time: 50ms --- Exception with Enum Data ---static void validateInput(String value, String fieldName)
94static void validateInput(String value(empty), String fieldNameemail) { //?validate_input_method95 if (value == null || value.isBlank()) { //?check_blankif (value == null || value.isBlank())
94static void validateInput(String value, String fieldName) { //?validate_input_method95 if (value(empty) == null || value.isBlank()) { //?check_blank96 throw new ValidationException(fieldName, ValidationErrorType.REQUIRED); //?throw_required97 }this.fieldName ← email, this.errorType ← REQUIRED
161public ValidationException(String fieldNameemail, ValidationErrorType errorTypeREQUIRED) { //?validation_constructor162 super(fieldName + ": " + errorType.name().toLowerCase().replace('_', ' ')); //?validation_message163 this.fieldName→ email = fieldNameemail; //?set_field_name164 this.errorType→ REQUIRED = errorTypeREQUIRED; //?set_error_type165}catch (ValidationException e)
37 validateInput("", "email"); //?call_validate_input38} catch (ValidationException eValidationException: email: required) { //?catch_validate39 System.out.println("Validation failed!"); //?print_validate_failed40 System.out.println(" Message: " + e.getMessage()); //?print_validate_message41 System.out.println(" Field: " + e.getFieldName()); //?print_field42 System.out.println(" Error type: " + e.getErrorType()); //?print_error_typeoutputValidation failed! Message: email: requiredpublic String getFieldName()
167public String getFieldName() { //?get_field_name168 return fieldNameemail;169}System.out.println(" Field: " + e.getFieldName()); //?print_field
40System.out.println(" Message: " + e.getMessage()); //?print_validate_message41System.out.println(" Field: " + e.getFieldName()); //?print_field42System.out.println(" Error type: " + e.getErrorType()); //?print_error_type43System.out.println(" Is required error? " + e.isRequiredError()); //?print_is_requiredoutput Field: emailpublic ValidationErrorType getErrorType()
171public ValidationErrorType getErrorType() { //?get_error_type172 return errorTypeREQUIRED;173}System.out.println(" Error type: " + e.getErrorType()); //?print_erro…
41 System.out.println(" Field: " + e.getFieldName()); //?print_field42 System.out.println(" Error type: " + e.getErrorType()); //?print_error_type43 System.out.println(" Is required error? " + e.isRequiredError()); //?print_is_required44}output Error type: REQUIREDpublic boolean isRequiredError()
175public boolean isRequiredError() { //?is_required176 return errorTypeREQUIRED == ValidationErrorType.REQUIRED; //?check_required177}balance ← 100.0
42 System.out.println(" Error type: " + e.getErrorType()); //?print_error_type43 System.out.println(" Is required error? " + e.isRequiredError()); //?print_is_required44}4546// Using exception data for recovery //?recovery47System.out.println("\n--- Using Data for Recovery ---");4849double balance→ 100.0 = 100.00; //?initial_balance50double[] amounts = {50.00, 75.00, 30.00}; //?amountsoutput Is required error? true --- Using Data for Recovery ---for (double amount : amounts)
pass 1 of 352for (double amount50.0 : amounts) { //?loop_amounts53 try { //?try_withdrawAll 3 passes — pass 1 is the card above pass amountbalancerequestedAmountethis.balancethis.requestedAmount1 50.0 — — — — — 2 75.0 50.0 75.0 InsufficientFundsException: Insufficient funds: balance=$50.00, requested=$75.00 50.0 75.0 3 30.0 — — — — — try
pass 1 of 352for (double amount : amounts) { //?loop_amounts53 try { //?try_withdraw54 withdraw(balance100.0, amount50.0); //?call_withdraw55 balance -= amount; //?update_balanceAll 3 passes — pass 1 is the card above pass balanceamountrequestedAmountethis.balancethis.requestedAmount1 100.0 50.0 — — — — 2 50.0 75.0 75.0 InsufficientFundsException: Insufficient funds: balance=$50.00, requested=$75.00 50.0 75.0 3 50.0 30.0 — — — — balance ← 50.0
pass 1 of 353 try { //?try_withdraw54 withdraw(balance100.0, amount50.0); //?call_withdraw55 balance→ 50.0 -= amount50.0; //?update_balance56 System.out.println("✓ Withdrew $" + amount50.0 + ", new balance: $" + balance50.0); //?print_success57 } catch (InsufficientFundsException e) { //?catch_withdraw58 System.out.println("✗ Cannot withdraw $" + amount); //?print_cannot59 System.out.println(" Suggestion: withdraw up to $" + e.getBalance()); //?print_suggestion60 }61 }6263 System.out.println("\n=== Key Points ===");64 System.out.println("""65 1. Add fields to store relevant error data66 2. Provide getters to access the data67 3. Include data in the message for readability68 4. Caller can use data for error recovery69 5. Consider computed properties (like shortfall)70 """);71}7273static void transferMoney(double balance, double amount) { //?transfer_method74 if (amount > balance) { //?check_funds75 throw new InsufficientFundsException(balance, amount); //?throw_insufficient76 }77 System.out.println("Transfer of $" + amount + " successful"); //?transfer_success78}7980static void withdraw(double balance100.0, double amount50.0) { //?withdraw_method81 if (amount > balance) { //?check_withdrawoutput✓ Withdrew $50.0, new balance: $50.0All 3 passes — pass 1 is the card above pass amountrequestedAmountebalancethis.balancethis.requestedAmount1 50.0 — — 100.0 → 50.0 — — 2 75.0 75.0 InsufficientFundsException: Insufficient funds: balance=$50.00, requested=$75.00 50.0 50.0 75.0 3 30.0 — — 50.0 → 20.0 — — if (amount > balance)
80static void withdraw(double balance, double amount) { //?withdraw_method81 if (amount75.0 > balance50.0) { //?check_withdraw82 throw new InsufficientFundsException(balance, amount); //?throw_withdraw83 }this.balance ← 50.0, this.requestedAmount ← 75.0
pass 2 of 2106public InsufficientFundsException(double balance50.0, double requestedAmount75.0) { //?insufficient_constructor107 super(String.format( //?format_message108 "Insufficient funds: balance=$%.2f, requested=$%.2f",109 balance, requestedAmount110 ));111 this.balance→ 50.0 = balance50.0; //?set_balance112 this.requestedAmount→ 75.0 = requestedAmount75.0; //?set_requested113}catch (InsufficientFundsException e)
56 System.out.println("✓ Withdrew $" + amount + ", new balance: $" + balance); //?print_success57} catch (InsufficientFundsException eInsufficientFundsException: Insufficient funds: balance=$50.00, requested=$75.00) { //?catch_withdraw58 System.out.println("✗ Cannot withdraw $" + amount75.0); //?print_cannot59 System.out.println(" Suggestion: withdraw up to $" + e.getBalance()); //?print_suggestion60}output✗ Cannot withdraw $75.0public double getBalance()
pass 2 of 2115public double getBalance() { //?get_balance116 return balance50.0;117}System.out.println(" Suggestion: withdraw up to $" + e.getBalance());…
58 System.out.println("✗ Cannot withdraw $" + amount); //?print_cannot59 System.out.println(" Suggestion: withdraw up to $" + e.getBalance()); //?print_suggestion60}output Suggestion: withdraw up to $50.0
transferAmount ← 75.0
3public class ExceptionWithData {4 public static void main(String[] args) {5 System.out.println("=== Custom Exceptions with Data ===\n");67 // Exception with numeric data8 System.out.println("--- Exception with Numeric Data ---");910 double transferAmount→ 75.0 = 75.00;11 try {output=== Custom Exceptions with Data === --- Exception with Numeric Data ---try
10double transferAmount = 75.00;11try {12 transferMoney(100.00, transferAmount75.0);13} catch (InsufficientFundsException e) {static void transferMoney(double balance, double amount)
11 try {12 transferMoney(100.00, transferAmount75.0);13 } catch (InsufficientFundsException e) {14 System.out.println("Transfer failed!");15 System.out.println(" Message: " + e.getMessage());16 System.out.println(" Balance: $" + e.getBalance());17 System.out.println(" Requested: $" + e.getRequestedAmount());18 System.out.println(" Shortfall: $" + e.getShortfall());19 }2021 // Exception with string data22 System.out.println("\n--- Exception with String Data ---");2324 try {25 findUser("unknown_user");26 } catch (UserNotFoundException e) {27 System.out.println("User lookup failed!");28 System.out.println(" Message: " + e.getMessage());29 System.out.println(" Username: " + e.getUsername());30 System.out.println(" Search time: " + e.getSearchTimeMs() + "ms");31 }3233 // Exception with enum data34 System.out.println("\n--- Exception with Enum Data ---");3536 try {37 validateInput("", "email");38 } catch (ValidationException e) {39 System.out.println("Validation failed!");40 System.out.println(" Message: " + e.getMessage());41 System.out.println(" Field: " + e.getFieldName());42 System.out.println(" Error type: " + e.getErrorType());43 System.out.println(" Is required error? " + e.isRequiredError());44 }4546 // Using exception data for recovery47 System.out.println("\n--- Using Data for Recovery ---");4849 double balance = 100.00;50 double[] amounts = {50.00, 75.00, 30.00};5152 for (double amount : amounts) {53 try {54 withdraw(balance, amount);55 balance -= amount;56 System.out.println("✓ Withdrew $" + amount + ", new balance: $" + balance);57 } catch (InsufficientFundsException e) {58 System.out.println("✗ Cannot withdraw $" + amount);59 System.out.println(" Suggestion: withdraw up to $" + e.getBalance());60 }61 }6263 System.out.println("\n=== Key Points ===");64 System.out.println("""65 1. Add fields to store relevant error data66 2. Provide getters to access the data67 3. Include data in the message for readability68 4. Caller can use data for error recovery69 5. Consider computed properties (like shortfall)70 """);71}7273static void transferMoney(double balance100.0, double amount75.0) {74 if (amount > balance) {75 throw new InsufficientFundsException(balance, amount);76 }77 System.out.println("Transfer of $" + amount75.0 + " successful");78}outputTransfer of $75.0 successful --- Exception with String Data ---searchTime ← 50
86static void findUser(String usernameunknown_user) {87 // Simulate search with a fixed duration88 long searchTime→ 50 = 50;8990 // User not found91 throw new UserNotFoundException(username, searchTime);92}this.username ← unknown_user, this.searchTimeMs ← 50
133public UserNotFoundException(String usernameunknown_user, long searchTimeMs50) {134 super("User not found: " + username);135 this.username→ unknown_user = usernameunknown_user;136 this.searchTimeMs→ 50 = searchTimeMs50;137}catch (UserNotFoundException e)
25 findUser("unknown_user");26} catch (UserNotFoundException eUserNotFoundException: User not found: unknown_user) {27 System.out.println("User lookup failed!");28 System.out.println(" Message: " + e.getMessage());29 System.out.println(" Username: " + e.getUsername());30 System.out.println(" Search time: " + e.getSearchTimeMs() + "ms");outputUser lookup failed! Message: User not found: unknown_userpublic String getUsername()
139public String getUsername() {140 return usernameunknown_user;141}System.out.println(" Username: " + e.getUsername());
28 System.out.println(" Message: " + e.getMessage());29 System.out.println(" Username: " + e.getUsername());30 System.out.println(" Search time: " + e.getSearchTimeMs() + "ms");31}output Username: unknown_userpublic long getSearchTimeMs()
143public long getSearchTimeMs() {144 return searchTimeMs50;145}System.out.println(" Search time: " + e.getSearchTimeMs() + "ms");
29 System.out.println(" Username: " + e.getUsername());30 System.out.println(" Search time: " + e.getSearchTimeMs() + "ms");31}3233// Exception with enum data34System.out.println("\n--- Exception with Enum Data ---");output Search time: 50ms --- Exception with Enum Data ---static void validateInput(String value, String fieldName)
94static void validateInput(String value(empty), String fieldNameemail) {95 if (value == null || value.isBlank()) {if (value == null || value.isBlank())
94static void validateInput(String value, String fieldName) {95 if (value(empty) == null || value.isBlank()) {96 throw new ValidationException(fieldName, ValidationErrorType.REQUIRED);97 }this.fieldName ← email, this.errorType ← REQUIRED
161public ValidationException(String fieldNameemail, ValidationErrorType errorTypeREQUIRED) {162 super(fieldName + ": " + errorType.name().toLowerCase().replace('_', ' '));163 this.fieldName→ email = fieldNameemail;164 this.errorType→ REQUIRED = errorTypeREQUIRED;165}catch (ValidationException e)
37 validateInput("", "email");38} catch (ValidationException eValidationException: email: required) {39 System.out.println("Validation failed!");40 System.out.println(" Message: " + e.getMessage());41 System.out.println(" Field: " + e.getFieldName());42 System.out.println(" Error type: " + e.getErrorType());outputValidation failed! Message: email: requiredpublic String getFieldName()
167public String getFieldName() {168 return fieldNameemail;169}System.out.println(" Field: " + e.getFieldName());
40System.out.println(" Message: " + e.getMessage());41System.out.println(" Field: " + e.getFieldName());42System.out.println(" Error type: " + e.getErrorType());43System.out.println(" Is required error? " + e.isRequiredError());output Field: emailpublic ValidationErrorType getErrorType()
171public ValidationErrorType getErrorType() {172 return errorTypeREQUIRED;173}System.out.println(" Error type: " + e.getErrorType());
41 System.out.println(" Field: " + e.getFieldName());42 System.out.println(" Error type: " + e.getErrorType());43 System.out.println(" Is required error? " + e.isRequiredError());44}output Error type: REQUIREDpublic boolean isRequiredError()
175public boolean isRequiredError() {176 return errorTypeREQUIRED == ValidationErrorType.REQUIRED;177}balance ← 100.0
42 System.out.println(" Error type: " + e.getErrorType());43 System.out.println(" Is required error? " + e.isRequiredError());44}4546// Using exception data for recovery47System.out.println("\n--- Using Data for Recovery ---");4849double balance→ 100.0 = 100.00;50double[] amounts = {50.00, 75.00, 30.00};output Is required error? true --- Using Data for Recovery ---for (double amount : amounts)
pass 1 of 352for (double amount50.0 : amounts) {53 try {All 3 passes — pass 1 is the card above pass amountbalancerequestedAmountethis.balancethis.requestedAmount1 50.0 — — — — — 2 75.0 50.0 75.0 InsufficientFundsException: Insufficient funds: balance=$50.00, requested=$75.00 50.0 75.0 3 30.0 — — — — — try
pass 1 of 352for (double amount : amounts) {53 try {54 withdraw(balance100.0, amount50.0);55 balance -= amount;All 3 passes — pass 1 is the card above pass balanceamountrequestedAmountethis.balancethis.requestedAmount1 100.0 50.0 — — — — 2 50.0 75.0 75.0 InsufficientFundsException: Insufficient funds: balance=$50.00, requested=$75.00 50.0 75.0 3 50.0 30.0 — — — — balance ← 50.0
pass 1 of 353 try {54 withdraw(balance100.0, amount50.0);55 balance→ 50.0 -= amount50.0;56 System.out.println("✓ Withdrew $" + amount50.0 + ", new balance: $" + balance50.0);57 } catch (InsufficientFundsException e) {58 System.out.println("✗ Cannot withdraw $" + amount);59 System.out.println(" Suggestion: withdraw up to $" + e.getBalance());60 }61 }6263 System.out.println("\n=== Key Points ===");64 System.out.println("""65 1. Add fields to store relevant error data66 2. Provide getters to access the data67 3. Include data in the message for readability68 4. Caller can use data for error recovery69 5. Consider computed properties (like shortfall)70 """);71}7273static void transferMoney(double balance, double amount) {74 if (amount > balance) {75 throw new InsufficientFundsException(balance, amount);76 }77 System.out.println("Transfer of $" + amount + " successful");78}7980static void withdraw(double balance100.0, double amount50.0) {81 if (amount > balance) {output✓ Withdrew $50.0, new balance: $50.0All 3 passes — pass 1 is the card above pass amountrequestedAmountebalancethis.balancethis.requestedAmount1 50.0 — — 100.0 → 50.0 — — 2 75.0 75.0 InsufficientFundsException: Insufficient funds: balance=$50.00, requested=$75.00 50.0 50.0 75.0 3 30.0 — — 50.0 → 20.0 — — if (amount > balance)
80static void withdraw(double balance, double amount) {81 if (amount75.0 > balance50.0) {82 throw new InsufficientFundsException(balance, amount);83 }this.balance ← 50.0, this.requestedAmount ← 75.0
106public InsufficientFundsException(double balance50.0, double requestedAmount75.0) {107 super(String.format(108 "Insufficient funds: balance=$%.2f, requested=$%.2f",109 balance, requestedAmount110 ));111 this.balance→ 50.0 = balance50.0;112 this.requestedAmount→ 75.0 = requestedAmount75.0;113}catch (InsufficientFundsException e)
56 System.out.println("✓ Withdrew $" + amount + ", new balance: $" + balance);57} catch (InsufficientFundsException eInsufficientFundsException: Insufficient funds: balance=$50.00, requested=$75.00) {58 System.out.println("✗ Cannot withdraw $" + amount75.0);59 System.out.println(" Suggestion: withdraw up to $" + e.getBalance());60}output✗ Cannot withdraw $75.0public double getBalance()
115public double getBalance() {116 return balance50.0;117}System.out.println(" Suggestion: withdraw up to $" + e.getBalance());
58 System.out.println("✗ Cannot withdraw $" + amount);59 System.out.println(" Suggestion: withdraw up to $" + e.getBalance());60}output Suggestion: withdraw up to $50.0
Add fields, constructor, getters. Carry context about what went wrong.
Exception hierarchy
Organize related exceptions into a hierarchy.
// Building an Exception Class Hierarchy
public class ExceptionHierarchy {
public static void main(String[] args) {
System.out.println("=== Exception Hierarchy ===\n");
// Show the hierarchy
System.out.println("--- Our Custom Hierarchy ---");
System.out.println("""
AppException (base)
├── DataException
│ ├── DataNotFoundException
│ └── DataValidationException
└── ServiceException
├── AuthenticationException
└── AuthorizationException
""");
// Catch specific exception
System.out.println("--- Catch Specific Exception ---");
try {
findProduct(999);
} catch (DataNotFoundException e) {
System.out.println("Caught DataNotFoundException: " + e.getMessage());
System.out.println(" Entity: " + e.getEntityType());
System.out.println(" ID: " + e.getEntityId());
}
// Catch parent exception
System.out.println("\n--- Catch Parent Exception (DataException) ---");
try {
validateProduct("", -10);
} catch (DataException e) {
System.out.println("Caught DataException: " + e.getClass().getSimpleName());
System.out.println(" Message: " + e.getMessage());
System.out.println(" Error Code: " + e.getErrorCode());
}
// Catch base exception
System.out.println("\n--- Catch Base Exception (AppException) ---");
String[] operations = {"find", "validate", "auth", "authz"};
for (String op : operations) {
try {
performOperation(op);
} catch (AppException e) {
System.out.println("[" + e.getErrorCode() + "] " +
e.getClass().getSimpleName() + ": " + e.getMessage());
}
}
// Check exception type
System.out.println("\n--- Check Exception Type ---");
try {
performOperation("auth");
} catch (AppException e) {
if (e instanceof AuthenticationException) {
System.out.println("Authentication failed - redirect to login");
} else if (e instanceof AuthorizationException) {
System.out.println("Not authorized - show error page");
} else if (e instanceof DataException) {
System.out.println("Data error - retry or notify user");
}
}
System.out.println("\n=== Key Points ===");
System.out.println("""
1. Create a base exception for your application
2. Group related exceptions under category classes
3. Include common fields in base (like errorCode)
4. Catch at appropriate level of specificity
5. Use instanceof to check type when needed
""");
}
static void findProduct(int id) {
throw new DataNotFoundException("Product", id);
}
static void validateProduct(String name, double price) {
if (name.isBlank()) {
throw new DataValidationException("Product name is required");
}
if (price < 0) {
throw new DataValidationException("Price cannot be negative");
}
}
static void performOperation(String operation) {
switch (operation) {
case "find" -> throw new DataNotFoundException("User", 1);
case "validate" -> throw new DataValidationException("Invalid data");
case "auth" -> throw new AuthenticationException("Invalid credentials");
case "authz" -> throw new AuthorizationException("admin", "DELETE");
}
}
}
// Base exception for the application
abstract class AppException extends RuntimeException {
private final String errorCode;
public AppException(String errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
public AppException(String errorCode, String message, Throwable cause) {
super(message, cause);
this.errorCode = errorCode;
}
public String getErrorCode() {
return errorCode;
}
}
// Category: Data-related exceptions
abstract class DataException extends AppException {
public DataException(String errorCode, String message) {
super(errorCode, message);
}
}
// Specific: Entity not found
class DataNotFoundException extends DataException {
private final String entityType;
private final Object entityId;
public DataNotFoundException(String entityType, Object entityId) {
super("NOT_FOUND", entityType + " not found with id: " + entityId);
this.entityType = entityType;
this.entityId = entityId;
}
public String getEntityType() { return entityType; }
public Object getEntityId() { return entityId; }
}
// Specific: Validation error
class DataValidationException extends DataException {
public DataValidationException(String message) {
super("VALIDATION_ERROR", message);
}
}
// Category: Service-related exceptions
abstract class ServiceException extends AppException {
public ServiceException(String errorCode, String message) {
super(errorCode, message);
}
}
// Specific: Authentication failure
class AuthenticationException extends ServiceException {
public AuthenticationException(String message) {
super("AUTH_FAILED", message);
}
}
// Specific: Authorization failure
class AuthorizationException extends ServiceException {
private final String requiredRole;
private final String action;
public AuthorizationException(String requiredRole, String action) {
super("ACCESS_DENIED",
"Access denied: requires " + requiredRole + " role for " + action);
this.requiredRole = requiredRole;
this.action = action;
}
public String getRequiredRole() { return requiredRole; }
public String getAction() { return action; }
}
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
public static void main(String[] args)
3public class ExceptionHierarchy {4 public static void main(String[] args) {5 System.out.println("=== Exception Hierarchy ===\n");67 // Show the hierarchy //?show_hierarchy8 System.out.println("--- Our Custom Hierarchy ---");9 System.out.println("""10 AppException (base)11 ├── DataException12 │ ├── DataNotFoundException13 │ └── DataValidationException14 └── ServiceException15 ├── AuthenticationException16 └── AuthorizationException17 """);1819 // Catch specific exception //?catch_specific20 System.out.println("--- Catch Specific Exception ---");output=== Exception Hierarchy === --- Our Custom Hierarchy --- AppException (base) ├── DataException │ ├── DataNotFoundException │ └── DataValidationException └── ServiceException ├── AuthenticationException └── AuthorizationException --- Catch Specific Exception ---static void findProduct(int id)
80static void findProduct(int id999) { //?find_product_method81 throw new DataNotFoundException("Product", id); //?throw_not_found82}this.errorCode ← NOT_FOUND
pass 1 of 7107public AppException(String errorCodeNOT_FOUND, String messageProduct not found with id: 999) { //?app_constructor108 super(message); //?super_message109 this.errorCode→ NOT_FOUND = errorCodeNOT_FOUND; //?set_error_code110}All 7 passes — pass 1 is the card above pass errorCodemessageentityTypeentityIderequiredRoleactionthis.errorCodethis.entityTypethis.entityIdthis.requiredRolethis.action1 NOT_FOUND Product not found with id: 999 Product 999 DataNotFoundException: Product not found with id: 999 — — NOT_FOUND Product 999 — — 2 VALIDATION_ERROR Product name is required — — DataValidationException: Product name is required — — VALIDATION_ERROR — — — — 3 NOT_FOUND User not found with id: 1 User 1 — — — NOT_FOUND User 1 — — 4 VALIDATION_ERROR Invalid data — — — — — VALIDATION_ERROR — — — — 5 AUTH_FAILED Invalid credentials — — — — — AUTH_FAILED — — — — 6 ACCESS_DENIED Access denied: requires admin role for DELETE — — — admin DELETE ACCESS_DENIED — — admin DELETE 7 AUTH_FAILED Invalid credentials — — AuthenticationException: Invalid credentials — — AUTH_FAILED — — — — public DataException(String errorCode, String message)
pass 1 of 4123abstract class DataException extends AppException { //?data_exception_def124 public DataException(String errorCodeNOT_FOUND, String messageProduct not found with id: 999) { //?data_constructor125 super(errorCode, message); //?data_superAll 4 passes — pass 1 is the card above pass errorCodemessageentityTypeentityIdethis.entityTypethis.entityId1 NOT_FOUND Product not found with id: 999 Product 999 DataNotFoundException: Product not found with id: 999 Product 999 2 VALIDATION_ERROR Product name is required — — DataValidationException: Product name is required — — 3 NOT_FOUND User not found with id: 1 User 1 — User 1 4 VALIDATION_ERROR Invalid data — — — — — this.entityType ← Product, this.entityId ← 999
pass 1 of 2134public DataNotFoundException(String entityTypeProduct, Object entityId999) { //?not_found_constructor135 super("NOT_FOUND", entityType + " not found with id: " + entityId); //?not_found_super136 this.entityType→ Product = entityTypeProduct; //?set_entity_type137 this.entityId→ 999 = entityId999; //?set_entity_id138}catch (DataNotFoundException e)
23 findProduct(999); //?call_find_product24} catch (DataNotFoundException eDataNotFoundException: Product not found with id: 999) { //?catch_not_found25 System.out.println("Caught DataNotFoundException: " + e.getMessage()); //?print_not_found26 System.out.println(" Entity: " + e.getEntityType()); //?print_entity27 System.out.println(" ID: " + e.getEntityId()); //?print_idoutputCaught DataNotFoundException: Product not found with id: 999public String getEntityType()
140public String getEntityType() { return entityTypeProduct; } //?get_entity_type141public Object getEntityId() { return entityId; } //?get_entity_idSystem.out.println(" Entity: " + e.getEntityType()); //?print_entity
25 System.out.println("Caught DataNotFoundException: " + e.getMessage()); //?print_not_found26 System.out.println(" Entity: " + e.getEntityType()); //?print_entity27 System.out.println(" ID: " + e.getEntityId()); //?print_id28}output Entity: Productpublic Object getEntityId()
140 public String getEntityType() { return entityType; } //?get_entity_type141 public Object getEntityId() { return entityId999; } //?get_entity_id142}System.out.println(" ID: " + e.getEntityId()); //?print_id
26 System.out.println(" Entity: " + e.getEntityType()); //?print_entity27 System.out.println(" ID: " + e.getEntityId()); //?print_id28}2930// Catch parent exception //?catch_parent31System.out.println("\n--- Catch Parent Exception (DataException) ---");output ID: 999 --- Catch Parent Exception (DataException) ---static void validateProduct(String name, double price)
84static void validateProduct(String name(empty), double price-10.0) { //?validate_product_method85 if (name.isBlank()) { //?check_namepublic DataValidationException(String message)
pass 1 of 2145class DataValidationException extends DataException { //?validation_def146 public DataValidationException(String messageProduct name is required) { //?validation_constructor147 super("VALIDATION_ERROR", message); //?validation_supercatch (DataException e)
34 validateProduct("", -10); //?call_validate35} catch (DataException eDataValidationException: Product name is required) { //?catch_data_exception36 System.out.println("Caught DataException: " + e.getClass().getSimpleName()); //?print_class37 System.out.println(" Message: " + e.getMessage()); //?print_parent_msg38 System.out.println(" Error Code: " + e.getErrorCode()); //?print_error_code39}outputCaught DataException: DataValidationException Message: Product name is requiredpublic String getErrorCode()
pass 1 of 5117public String getErrorCode() { //?get_error_code118 return errorCodeVALIDATION_ERROR;119}All 5 passes — pass 1 is the card above pass errorCode1 VALIDATION_ERROR 2 NOT_FOUND 3 VALIDATION_ERROR 4 AUTH_FAILED 5 ACCESS_DENIED String[] operations = {"find", "validate", "auth", "authz"}; //?operat…
37 System.out.println(" Message: " + e.getMessage()); //?print_parent_msg38 System.out.println(" Error Code: " + e.getErrorCode()); //?print_error_code39}4041// Catch base exception //?catch_base42System.out.println("\n--- Catch Base Exception (AppException) ---");4344String[] operations = {"find", "validate", "auth", "authz"}; //?operationsoutput Error Code: VALIDATION_ERROR --- Catch Base Exception (AppException) ---for (String op : operations)
pass 1 of 446for (String opfind : operations) { //?loop_ops47 try { //?try_opAll 4 passes — pass 1 is the card above pass opentityTypeentityIdmessagerequiredRoleactionthis.entityTypethis.entityIdthis.requiredRolethis.action1 find User 1 — — — User 1 — — 2 validate — — Invalid data — — — — — — 3 auth — — Invalid credentials — — — — — — 4 authz — — — admin DELETE — — admin DELETE try
pass 1 of 446for (String op : operations) { //?loop_ops47 try { //?try_op48 performOperation(opfind); //?call_perform49 } catch (AppException e) { //?catch_appAll 4 passes — pass 1 is the card above pass opentityTypeentityIdmessagerequiredRoleactionthis.entityTypethis.entityIdthis.requiredRolethis.action1 find User 1 — — — User 1 — — 2 validate — — Invalid data — — — — — — 3 auth — — Invalid credentials — — — — — — 4 authz — — — admin DELETE — — admin DELETE static void performOperation(String operation)
pass 1 of 593static void performOperation(String operationfind) { //?perform_op_method94 switch (operation) { //?switch_opAll 5 passes — pass 1 is the card above pass operationentityTypeentityIdmessagerequiredRoleactionethis.entityTypethis.entityIdthis.requiredRolethis.action1 find User 1 — — — — User 1 — — 2 validate — — Invalid data — — — — — — — 3 auth — — Invalid credentials — — — — — — — 4 authz — — — admin DELETE — — — admin DELETE 5 auth — — Invalid credentials — — AuthenticationException: Invalid credentials — — — — this.entityType ← User, this.entityId ← 1
pass 2 of 2134public DataNotFoundException(String entityTypeUser, Object entityId1) { //?not_found_constructor135 super("NOT_FOUND", entityType + " not found with id: " + entityId); //?not_found_super136 this.entityType→ User = entityTypeUser; //?set_entity_type137 this.entityId→ 1 = entityId1; //?set_entity_id138}catch (AppException e)
pass 1 of 448 performOperation(op); //?call_perform49} catch (AppException eDataNotFoundException: User not found with id: 1) { //?catch_app50 System.out.println("[" + e.getErrorCode() + "] " + //?print_app_error51 e.getClass().getSimpleName() + ": " + e.getMessage());52}All 4 passes — pass 1 is the card above pass e1 DataNotFoundException: User not found with id: 1 2 DataValidationException: Invalid data 3 AuthenticationException: Invalid credentials 4 AuthorizationException: Access denied: requires admin role for DELETE System.out.println("[" + e.getErrorCode() + "] " + //?print_app_error
49} catch (AppException e) { //?catch_app50 System.out.println("[" + e.getErrorCode() + "] " + //?print_app_error51 e.getClass().getSimpleName() + ": " + e.getMessage());52}output[NOT_FOUND] DataNotFoundException: User not found with id: 1public DataValidationException(String message)
pass 2 of 2145class DataValidationException extends DataException { //?validation_def146 public DataValidationException(String messageInvalid data) { //?validation_constructor147 super("VALIDATION_ERROR", message); //?validation_superSystem.out.println("[" + e.getErrorCode() + "] " + //?print_app_error
49} catch (AppException e) { //?catch_app50 System.out.println("[" + e.getErrorCode() + "] " + //?print_app_error51 e.getClass().getSimpleName() + ": " + e.getMessage());52}output[VALIDATION_ERROR] DataValidationException: Invalid datapublic ServiceException(String errorCode, String message)
pass 1 of 3152abstract class ServiceException extends AppException { //?service_exception_def153 public ServiceException(String errorCodeAUTH_FAILED, String messageInvalid credentials) { //?service_constructor154 super(errorCode, message); //?service_superAll 3 passes — pass 1 is the card above pass errorCodemessagerequiredRoleactionethis.requiredRolethis.action1 AUTH_FAILED Invalid credentials — — — — — 2 ACCESS_DENIED Access denied: requires admin role for DELETE admin DELETE — admin DELETE 3 AUTH_FAILED Invalid credentials — — AuthenticationException: Invalid credentials — — public AuthenticationException(String message)
pass 1 of 2159class AuthenticationException extends ServiceException { //?auth_def160 public AuthenticationException(String messageInvalid credentials) { //?auth_constructor161 super("AUTH_FAILED", message); //?auth_superSystem.out.println("[" + e.getErrorCode() + "] " + //?print_app_error
49} catch (AppException e) { //?catch_app50 System.out.println("[" + e.getErrorCode() + "] " + //?print_app_error51 e.getClass().getSimpleName() + ": " + e.getMessage());52}output[AUTH_FAILED] AuthenticationException: Invalid credentialsthis.requiredRole ← admin, this.action ← DELETE
170public AuthorizationException(String requiredRoleadmin, String actionDELETE) { //?authz_constructor171 super("ACCESS_DENIED", //?authz_super172 "Access denied: requires " + requiredRole + " role for " + action);173 this.requiredRole→ admin = requiredRoleadmin; //?set_required_role174 this.action→ DELETE = actionDELETE; //?set_action175}System.out.println("[" + e.getErrorCode() + "] " + //?print_app_error
49 } catch (AppException e) { //?catch_app50 System.out.println("[" + e.getErrorCode() + "] " + //?print_app_error51 e.getClass().getSimpleName() + ": " + e.getMessage());52 }53}5455// Check exception type //?check_type56System.out.println("\n--- Check Exception Type ---");output[ACCESS_DENIED] AuthorizationException: Access denied: requires admin role for DELETE --- Check Exception Type ---public AuthenticationException(String message)
pass 2 of 2159class AuthenticationException extends ServiceException { //?auth_def160 public AuthenticationException(String messageInvalid credentials) { //?auth_constructor161 super("AUTH_FAILED", message); //?auth_supercatch (AppException e)
59 performOperation("auth"); //?call_auth_op60} catch (AppException eAuthenticationException: Invalid credentials) { //?catch_check61 if (e instanceof AuthenticationException) { //?instanceof_authif (e instanceof AuthenticationException)
60} catch (AppException e) { //?catch_check61 if (e instanceof AuthenticationException) { //?instanceof_auth62 System.out.println("Authentication failed - redirect to login"); //?redirect_login63 } else if (e instanceof AuthorizationException) { //?instanceof_authzoutputAuthentication failed - redirect to loginSystem.out.println(" === Key Points ===");
70 System.out.println("\n=== Key Points ===");71 System.out.println("""72 1. Create a base exception for your application73 2. Group related exceptions under category classes74 3. Include common fields in base (like errorCode)75 4. Catch at appropriate level of specificity76 5. Use instanceof to check type when needed77 """);78}output === Key Points === 1. Create a base exception for your application 2. Group related exceptions under category classes 3. Include common fields in base (like errorCode) 4. Catch at appropriate level of specificity 5. Use instanceof to check type when needed
Base exception class with specialized subclasses. Catch at any level.
Checked custom exception
Create exceptions that must be handled.
// Creating Checked Custom Exceptions
import java.io.IOException;
public class CheckedCustom {
public static void main(String[] args) {
System.out.println("=== Checked Custom Exceptions ===\n");
// Checked exception requires handling
System.out.println("--- Checked Exception Requires Handling ---");
try {
loadConfiguration("app.properties");
} catch (ConfigurationException e) {
System.out.println("Configuration error!");
System.out.println(" Message: " + e.getMessage());
System.out.println(" File: " + e.getConfigFile());
if (e.getCause() != null) {
System.out.println(" Cause: " + e.getCause().getMessage());
}
}
// Multiple checked exceptions
System.out.println("\n--- Multiple Checked Exceptions ---");
try {
connectToService("db.example.com", 5432);
} catch (ConnectionException e) {
System.out.println("Connection failed!");
System.out.println(" Host: " + e.getHost());
System.out.println(" Port: " + e.getPort());
} catch (ConfigurationException e) {
System.out.println("Configuration error: " + e.getMessage());
}
// When to use checked vs unchecked
System.out.println("\n--- When to Use Checked ---");
System.out.println("""
Use CHECKED custom exceptions when:
• Caller can reasonably recover
• External resource failure (files, network)
• Caller should be forced to handle
• Part of a public API contract
Use UNCHECKED custom exceptions when:
• Programming error (invalid arguments)
• Unrecoverable situation
• Caller typically cannot handle
• Would clutter code with try-catch
""");
// Demonstrate forced handling
System.out.println("--- Forced Handling Demo ---");
processConfiguration();
System.out.println("\n=== Key Points ===");
System.out.println("""
1. Extend Exception (not RuntimeException) for checked
2. Caller MUST use try-catch or declare throws
3. Good for recoverable external failures
4. Forces caller to think about error handling
5. Can still include additional data fields
""");
}
// Method that throws checked exception
static void loadConfiguration(String filename) throws ConfigurationException {
// Simulate file not found
throw new ConfigurationException(
"Configuration file not found",
filename,
new IOException("File does not exist: " + filename)
);
}
// Method that throws multiple checked exceptions
static void connectToService(String host, int port)
throws ConnectionException, ConfigurationException {
if (host == null || host.isEmpty()) {
throw new ConfigurationException("Host is required", "connection.properties");
}
// Simulate connection failure
throw new ConnectionException(host, port, "Connection refused");
}
static void processConfiguration() {
// Option 1: Handle with try-catch
System.out.println("Option 1: Handle with try-catch");
try {
loadConfiguration("settings.xml");
} catch (ConfigurationException e) {
System.out.println(" Handled: " + e.getMessage());
}
// Option 2: Would be to declare 'throws'
System.out.println("\nOption 2: Propagate with throws");
System.out.println(" void myMethod() throws ConfigurationException { ... }");
}
}
// Checked custom exception
class ConfigurationException extends Exception {
private final String configFile;
public ConfigurationException(String message, String configFile) {
super(message);
this.configFile = configFile;
}
public ConfigurationException(String message, String configFile, Throwable cause) {
super(message, cause);
this.configFile = configFile;
}
public String getConfigFile() {
return configFile;
}
}
// Another checked custom exception
class ConnectionException extends Exception {
private final String host;
private final int port;
public ConnectionException(String host, int port, String message) {
super(message);
this.host = host;
this.port = port;
}
public ConnectionException(String host, int port, String message, Throwable cause) {
super(message, cause);
this.host = host;
this.port = port;
}
public String getHost() { return host; }
public int getPort() { return port; }
public String getConnectionString() {
return host + ":" + port;
}
}
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
public static void main(String[] args)
5public class CheckedCustom {6 public static void main(String[] args) {7 System.out.println("=== Checked Custom Exceptions ===\n");89 // Checked exception requires handling //?checked_required10 System.out.println("--- Checked Exception Requires Handling ---");output=== Checked Custom Exceptions === --- Checked Exception Requires Handling ---static void loadConfiguration(String filename) throws ConfigurationExc…
pass 1 of 267// Method that throws checked exception //?throws_checked68static void loadConfiguration(String filenameapp.properties) throws ConfigurationException { //?load_config_method69 // Simulate file not found //?simulate_not_found70 throw new ConfigurationException( //?throw_config71 "Configuration file not found",72 filename,73 new IOException("File does not exist: " + filename)74 );75}this.configFile ← app.properties
pass 1 of 2111public ConfigurationException(String messageConfiguration file not found, String configFileapp.properties, Throwable causejava.io.IOException: File does not exist: app.properties) { //?config_constructor_cause112 super(message, cause); //?super_message_cause113 this.configFile→ app.properties = configFileapp.properties; //?set_config_file_cause114}catch (ConfigurationException e)
13 loadConfiguration("app.properties"); //?call_load_config14} catch (ConfigurationException eConfigurationException: Configuration file not found) { //?catch_config15 System.out.println("Configuration error!"); //?print_config_error16 System.out.println(" Message: " + e.getMessage()); //?print_config_msg17 System.out.println(" File: " + e.getConfigFile()); //?print_config_file18 if (e.getCause() != null) { //?check_causeoutputConfiguration error! Message: Configuration file not foundpublic String getConfigFile()
116public String getConfigFile() { //?get_config_file117 return configFileapp.properties;118}System.out.println(" File: " + e.getConfigFile()); //?print_config_fi…
16System.out.println(" Message: " + e.getMessage()); //?print_config_msg17System.out.println(" File: " + e.getConfigFile()); //?print_config_file18if (e.getCause() != null) { //?check_causeoutput File: app.propertiesif (e.getCause() != null)
17System.out.println(" File: " + e.getConfigFile()); //?print_config_file18if (e.getCause() != null) { //?check_cause19 System.out.println(" Cause: " + e.getCause().getMessage()); //?print_cause20}output Cause: File does not exist: app.propertiesSystem.out.println(" --- Multiple Checked Exceptions ---");
23// Multiple checked exceptions //?multiple_checked24System.out.println("\n--- Multiple Checked Exceptions ---");output --- Multiple Checked Exceptions ---static void connectToService(String host, int port) //?connect_method …
77// Method that throws multiple checked exceptions //?throws_multiple78static void connectToService(String hostdb.example.com, int port5432) //?connect_method79 throws ConnectionException, ConfigurationException { //?throws_clause80 if (host == null || host.isEmpty()) { //?check_host81 throw new ConfigurationException("Host is required", "connection.properties"); //?throw_host_error82 }83 // Simulate connection failure //?simulate_conn_fail84 throw new ConnectionException(host, port, "Connection refused"); //?throw_conn85}this.host ← db.example.com, this.port ← 5432
126public ConnectionException(String hostdb.example.com, int port5432, String messageConnection refused) { //?connection_constructor127 super(message); //?conn_super128 this.host→ db.example.com = hostdb.example.com; //?set_host129 this.port→ 5432 = port5432; //?set_port130}catch (ConnectionException e)
27 connectToService("db.example.com", 5432); //?call_connect28} catch (ConnectionException eConnectionException: Connection refused) { //?catch_connection29 System.out.println("Connection failed!"); //?print_conn_error30 System.out.println(" Host: " + e.getHost()); //?print_host31 System.out.println(" Port: " + e.getPort()); //?print_portoutputConnection failed!public String getHost()
138public String getHost() { return hostdb.example.com; } //?get_host139public int getPort() { return port; } //?get_portSystem.out.println(" Host: " + e.getHost()); //?print_host
29 System.out.println("Connection failed!"); //?print_conn_error30 System.out.println(" Host: " + e.getHost()); //?print_host31 System.out.println(" Port: " + e.getPort()); //?print_port32} catch (ConfigurationException e) { //?catch_config2output Host: db.example.compublic int getPort()
138public String getHost() { return host; } //?get_host139public int getPort() { return port5432; } //?get_portSystem.out.println(" Port: " + e.getPort()); //?print_port
30 System.out.println(" Host: " + e.getHost()); //?print_host31 System.out.println(" Port: " + e.getPort()); //?print_port32} catch (ConfigurationException e) { //?catch_config233 System.out.println("Configuration error: " + e.getMessage()); //?print_config234}3536// When to use checked vs unchecked //?when_to_use37System.out.println("\n--- When to Use Checked ---");38System.out.println("""39 Use CHECKED custom exceptions when:40 • Caller can reasonably recover41 • External resource failure (files, network)42 • Caller should be forced to handle43 • Part of a public API contract4445 Use UNCHECKED custom exceptions when:46 • Programming error (invalid arguments)47 • Unrecoverable situation48 • Caller typically cannot handle49 • Would clutter code with try-catch50 """);5152// Demonstrate forced handling //?forced_handling53System.out.println("--- Forced Handling Demo ---");5455processConfiguration(); //?call_process_configoutput Port: 5432 --- When to Use Checked --- Use CHECKED custom exceptions when: • Caller can reasonably recover • External resource failure (files, network) • Caller should be forced to handle • Part of a public API contract Use UNCHECKED custom exceptions when: • Programming error (invalid arguments) • Unrecoverable situation • Caller typically cannot handle • Would clutter code with try-catch --- Forced Handling Demo ---static void processConfiguration()
87static void processConfiguration() { //?process_config_method88 // Option 1: Handle with try-catch //?option189 System.out.println("Option 1: Handle with try-catch");90 try { //?try_processoutputOption 1: Handle with try-catchstatic void loadConfiguration(String filename) throws ConfigurationExc…
pass 2 of 267// Method that throws checked exception //?throws_checked68static void loadConfiguration(String filenamesettings.xml) throws ConfigurationException { //?load_config_method69 // Simulate file not found //?simulate_not_found70 throw new ConfigurationException( //?throw_config71 "Configuration file not found",72 filename,73 new IOException("File does not exist: " + filename)74 );75}this.configFile ← settings.xml
pass 2 of 2111public ConfigurationException(String messageConfiguration file not found, String configFilesettings.xml, Throwable causejava.io.IOException: File does not exist: settings.xml) { //?config_constructor_cause112 super(message, cause); //?super_message_cause113 this.configFile→ settings.xml = configFilesettings.xml; //?set_config_file_cause114}catch (ConfigurationException e)
91 loadConfiguration("settings.xml"); //?load_settings92} catch (ConfigurationException eConfigurationException: Configuration file not found) { //?catch_process93 System.out.println(" Handled: " + e.getMessage()); //?handled94}output Handled: Configuration file not foundSystem.out.println(" Option 2: Propagate with throws");
55 processConfiguration(); //?call_process_config5657 System.out.println("\n=== Key Points ===");58 System.out.println("""59 1. Extend Exception (not RuntimeException) for checked60 2. Caller MUST use try-catch or declare throws61 3. Good for recoverable external failures62 4. Forces caller to think about error handling63 5. Can still include additional data fields64 """);65}6667// Method that throws checked exception //?throws_checked68static void loadConfiguration(String filename) throws ConfigurationException { //?load_config_method69 // Simulate file not found //?simulate_not_found70 throw new ConfigurationException( //?throw_config71 "Configuration file not found",72 filename,73 new IOException("File does not exist: " + filename)74 );75}7677// Method that throws multiple checked exceptions //?throws_multiple78static void connectToService(String host, int port) //?connect_method79 throws ConnectionException, ConfigurationException { //?throws_clause80 if (host == null || host.isEmpty()) { //?check_host81 throw new ConfigurationException("Host is required", "connection.properties"); //?throw_host_error82 }83 // Simulate connection failure //?simulate_conn_fail84 throw new ConnectionException(host, port, "Connection refused"); //?throw_conn85}8687static void processConfiguration() { //?process_config_method88 // Option 1: Handle with try-catch //?option189 System.out.println("Option 1: Handle with try-catch");90 try { //?try_process91 loadConfiguration("settings.xml"); //?load_settings92 } catch (ConfigurationException e) { //?catch_process93 System.out.println(" Handled: " + e.getMessage()); //?handled94 }9596 // Option 2: Would be to declare 'throws' //?option297 System.out.println("\nOption 2: Propagate with throws");98 System.out.println(" void myMethod() throws ConfigurationException { ... }");99}output Option 2: Propagate with throws void myMethod() throws ConfigurationException { ... } === Key Points === 1. Extend Exception (not RuntimeException) for checked 2. Caller MUST use try-catch or declare throws 3. Good for recoverable external failures 4. Forces caller to think about error handling 5. Can still include additional data fields
Extend Exception for checked. Use for recoverable errors.
Best practices
Design exceptions effectively.
// Exception Design Best Practices
public class BestPractices {
public static void main(String[] args) {
System.out.println("=== Exception Design Best Practices ===\n");
// 1. Provide multiple constructors
System.out.println("--- 1. Multiple Constructors ---");
try {
throw new WellDesignedException("Simple message");
} catch (WellDesignedException e) {
System.out.println("Basic: " + e.getMessage());
}
try {
Exception cause = new Exception("Original error");
throw new WellDesignedException("With cause", cause);
} catch (WellDesignedException e) {
System.out.println("With cause: " + e.getMessage());
System.out.println(" Cause: " + e.getCause().getMessage());
}
// 2. Include context data
System.out.println("\n--- 2. Include Context Data ---");
try {
throw new OrderException("ORD-123", "Validation failed", OrderException.Stage.VALIDATION);
} catch (OrderException e) {
System.out.println("Order error: " + e.getMessage());
System.out.println(" Order ID: " + e.getOrderId());
System.out.println(" Stage: " + e.getStage());
}
// 3. Use meaningful names
System.out.println("\n--- 3. Use Meaningful Names ---");
System.out.println("""
Good names (clear what went wrong):
✓ InsufficientFundsException
✓ UserNotFoundException
✓ InvalidEmailFormatException
✓ OrderAlreadyShippedException
Bad names (vague or generic):
✗ BadDataException
✗ MyException
✗ Error1
✗ ProcessingException
""");
// 4. Document your exceptions
System.out.println("--- 4. Document with @throws ---");
System.out.println("""
/**
* Processes the order.
* @param orderId the order to process
* @throws OrderNotFoundException if order doesn't exist
* @throws OrderAlreadyProcessedException if already processed
*/
void processOrder(String orderId) { ... }
""");
// 5. Preserve the cause chain
System.out.println("--- 5. Preserve Cause Chain ---");
try {
simulateDatabaseError();
} catch (DataAccessException e) {
System.out.println("Exception chain:");
Throwable current = e;
int level = 0;
while (current != null) {
System.out.println(" " + " ".repeat(level) +
current.getClass().getSimpleName() + ": " + current.getMessage());
current = current.getCause();
level++;
}
}
// 6. Consider immutability
System.out.println("\n--- 6. Make Fields Final ---");
System.out.println("""
// Good: final fields
class MyException extends RuntimeException {
private final String code; // Cannot change
private final int value; // Cannot change
}
// Avoid: mutable state in exceptions
""");
System.out.println("\n=== Summary ===");
System.out.println("""
✓ Provide multiple constructors
✓ Include relevant context data
✓ Use clear, descriptive names
✓ Document in Javadoc with @throws
✓ Always preserve cause chain
✓ Make exception fields final
✓ Follow naming convention (*Exception)
""");
}
static void simulateDatabaseError() {
try {
throw new RuntimeException("Connection timeout");
} catch (RuntimeException e) {
throw new DataAccessException("Failed to execute query", e);
}
}
}
// Well-designed exception with multiple constructors
class WellDesignedException extends RuntimeException {
// Constructor 1: Message only
public WellDesignedException(String message) {
super(message);
}
// Constructor 2: Message + cause
public WellDesignedException(String message, Throwable cause) {
super(message, cause);
}
// Constructor 3: Cause only
public WellDesignedException(Throwable cause) {
super(cause);
}
}
// Exception with context data
class OrderException extends RuntimeException {
// Enum for processing stage
public enum Stage {
VALIDATION,
PAYMENT,
SHIPPING,
COMPLETION
}
private final String orderId;
private final Stage stage;
public OrderException(String orderId, String message, Stage stage) {
super(String.format("[%s] %s (stage: %s)", orderId, message, stage));
this.orderId = orderId;
this.stage = stage;
}
public OrderException(String orderId, String message, Stage stage, Throwable cause) {
super(String.format("[%s] %s (stage: %s)", orderId, message, stage), cause);
this.orderId = orderId;
this.stage = stage;
}
public String getOrderId() { return orderId; }
public Stage getStage() { return stage; }
}
// Exception that preserves cause chain
class DataAccessException extends RuntimeException {
public DataAccessException(String message) {
super(message);
}
public DataAccessException(String message, Throwable cause) {
super(message, cause);
}
}
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
public static void main(String[] args)
3public class BestPractices {4 public static void main(String[] args) {5 System.out.println("=== Exception Design Best Practices ===\n");67 // 1. Provide multiple constructors //?multiple_constructors8 System.out.println("--- 1. Multiple Constructors ---");output=== Exception Design Best Practices === --- 1. Multiple Constructors ---public WellDesignedException(String message)
116// Constructor 1: Message only //?constructor1117public WellDesignedException(String messageSimple message) { //?constructor_message118 super(message); //?super1catch (WellDesignedException e)
11 throw new WellDesignedException("Simple message"); //?throw_basic12} catch (WellDesignedException eWellDesignedException: Simple message) { //?catch_basic13 System.out.println("Basic: " + e.getMessage()); //?print_basic14}outputBasic: Simple messagecause ← java.lang.Exception: Original error
16try { //?try_with_cause17 Exception cause→ java.lang.Exception: Original error = new Exception("Original error"); //?create_cause18 throw new WellDesignedException("With cause", cause); //?throw_with_cause19} catch (WellDesignedException e) { //?catch_with_causepublic WellDesignedException(String message, Throwable cause)
121// Constructor 2: Message + cause //?constructor2122public WellDesignedException(String messageWith cause, Throwable causejava.lang.Exception: Original error) { //?constructor_cause123 super(message, cause); //?super2catch (WellDesignedException e)
18 throw new WellDesignedException("With cause", cause); //?throw_with_cause19} catch (WellDesignedException eWellDesignedException: With cause) { //?catch_with_cause20 System.out.println("With cause: " + e.getMessage()); //?print_with_cause21 System.out.println(" Cause: " + e.getCause().getMessage()); //?print_cause_msg22}outputWith cause: With cause Cause: Original errorSystem.out.println(" --- 2. Include Context Data ---");
24// 2. Include context data //?context_data25System.out.println("\n--- 2. Include Context Data ---");output --- 2. Include Context Data ---this.orderId ← ORD-123, this.stage ← VALIDATION
146public OrderException(String orderIdORD-123, String messageValidation failed, Stage stageVALIDATION) { //?order_constructor147 super(String.format("[%s] %s (stage: %s)", orderId, message, stage)); //?order_super148 this.orderId→ ORD-123 = orderIdORD-123; //?set_order_id149 this.stage→ VALIDATION = stageVALIDATION; //?set_stage150}catch (OrderException e)
28 throw new OrderException("ORD-123", "Validation failed", OrderException.Stage.VALIDATION); //?throw_context29} catch (OrderException eOrderException: [ORD-123] Validation failed (stage: VALIDATION)) { //?catch_context30 System.out.println("Order error: " + e.getMessage()); //?print_context31 System.out.println(" Order ID: " + e.getOrderId()); //?print_order_id32 System.out.println(" Stage: " + e.getStage()); //?print_stageoutputOrder error: [ORD-123] Validation failed (stage: VALIDATION)public String getOrderId()
158public String getOrderId() { return orderIdORD-123; } //?get_order_id159public Stage getStage() { return stage; } //?get_stageSystem.out.println(" Order ID: " + e.getOrderId()); //?print_order_id
30 System.out.println("Order error: " + e.getMessage()); //?print_context31 System.out.println(" Order ID: " + e.getOrderId()); //?print_order_id32 System.out.println(" Stage: " + e.getStage()); //?print_stage33}output Order ID: ORD-123public Stage getStage()
158 public String getOrderId() { return orderId; } //?get_order_id159 public Stage getStage() { return stageVALIDATION; } //?get_stage160}System.out.println(" Stage: " + e.getStage()); //?print_stage
31 System.out.println(" Order ID: " + e.getOrderId()); //?print_order_id32 System.out.println(" Stage: " + e.getStage()); //?print_stage33}3435// 3. Use meaningful names //?meaningful_names36System.out.println("\n--- 3. Use Meaningful Names ---");37System.out.println("""38 Good names (clear what went wrong):39 ✓ InsufficientFundsException40 ✓ UserNotFoundException41 ✓ InvalidEmailFormatException42 ✓ OrderAlreadyShippedException4344 Bad names (vague or generic):45 ✗ BadDataException46 ✗ MyException47 ✗ Error148 ✗ ProcessingException49 """);5051// 4. Document your exceptions //?document52System.out.println("--- 4. Document with @throws ---");53System.out.println("""54 /**55 * Processes the order.56 * @param orderId the order to process57 * @throws OrderNotFoundException if order doesn't exist58 * @throws OrderAlreadyProcessedException if already processed59 */60 void processOrder(String orderId) { ... }61 """);6263// 5. Preserve the cause chain //?preserve_cause64System.out.println("--- 5. Preserve Cause Chain ---");output Stage: VALIDATION --- 3. Use Meaningful Names --- Good names (clear what went wrong): ✓ InsufficientFundsException ✓ UserNotFoundException ✓ InvalidEmailFormatException ✓ OrderAlreadyShippedException Bad names (vague or generic): ✗ BadDataException ✗ MyException ✗ Error1 ✗ ProcessingException --- 4. Document with @throws --- /** * Processes the order. * @param orderId the order to process * @throws OrderNotFoundException if order doesn't exist * @throws OrderAlreadyProcessedException if already processed */ void processOrder(String orderId) { ... } --- 5. Preserve Cause Chain ---catch (RuntimeException e)
106 throw new RuntimeException("Connection timeout"); //?throw_timeout107} catch (RuntimeException ejava.lang.RuntimeException: Connection timeout) { //?catch_simulate108 throw new DataAccessException("Failed to execute query", e); //?throw_data_access109}public DataAccessException(String message, Throwable cause)
169public DataAccessException(String messageFailed to execute query, Throwable causejava.lang.RuntimeException: Connection timeout) { //?da_constructor_cause170 super(message, cause); //?da_super_causecurrent ← DataAccessException: Failed to execute query, level ← 0
67 simulateDatabaseError(); //?call_simulate68} catch (DataAccessException eDataAccessException: Failed to execute query) { //?catch_chain69 System.out.println("Exception chain:"); //?print_chain_header70 Throwable current→ DataAccessException: Failed to execute query = e; //?current_init71 int level→ 0 = 0; //?level_init72 while (current != null) { //?while_chainoutputException chain:current ← java.lang.RuntimeException: Connection timeout, level ← 1
pass 1 of 271int level = 0; //?level_init72while (currentDataAccessException: Failed to execute query != null) { //?while_chain73 System.out.println(" " + " ".repeat(level0) + //?print_chain74 current.getClass().getSimpleName() + ": " + current.getMessage());75 current→ java.lang.RuntimeException: Connection timeout = current.getCause(); //?next_cause76 level→ 1++; //?increment_level77}output DataAccessException: Failed to execute querycurrent ← null, level ← 2
pass 2 of 271int level = 0; //?level_init72while (currentjava.lang.RuntimeException: Connection timeout != null) { //?while_chain73 System.out.println(" " + " ".repeat(level1) + //?print_chain74 current.getClass().getSimpleName() + ": " + current.getMessage());75 current→ null = current.getCause(); //?next_cause76 level→ 2++; //?increment_level77}output RuntimeException: Connection timeoutSystem.out.println(" --- 6. Make Fields Final ---");
80 // 6. Consider immutability //?immutability81 System.out.println("\n--- 6. Make Fields Final ---");82 System.out.println("""83 // Good: final fields84 class MyException extends RuntimeException {85 private final String code; // Cannot change86 private final int value; // Cannot change87 }8889 // Avoid: mutable state in exceptions90 """);9192 System.out.println("\n=== Summary ===");93 System.out.println("""94 ✓ Provide multiple constructors95 ✓ Include relevant context data96 ✓ Use clear, descriptive names97 ✓ Document in Javadoc with @throws98 ✓ Always preserve cause chain99 ✓ Make exception fields final100 ✓ Follow naming convention (*Exception)101 """);102}output --- 6. Make Fields Final --- // Good: final fields class MyException extends RuntimeException { private final String code; // Cannot change private final int value; // Cannot change } // Avoid: mutable state in exceptions === Summary === ✓ Provide multiple constructors ✓ Include relevant context data ✓ Use clear, descriptive names ✓ Document in Javadoc with @throws ✓ Always preserve cause chain ✓ Make exception fields final ✓ Follow naming convention (*Exception)
Include cause, make immutable, provide useful messages.
Exercise: Practical.java
Build a banking system with domain-specific exceptions