Can you create custom exceptions?
A custom exception is a regular class that inherits from Exception or RuntimeException.
Junior Level
Yes, you can!
A custom exception is a regular class that inherits from Exception or RuntimeException.
Custom = created by you for your domain. Standard = from JDK (IOException, IllegalArgumentException). Custom ones carry domain meaning: OrderNotFoundException says more than IllegalArgumentException.
// Checked exception
public class UserNotFoundException extends Exception {
public UserNotFoundException(String message) {
super(message);
}
}
// Unchecked exception
public class InsufficientFundsException extends RuntimeException {
public InsufficientFundsException(String message) {
super(message);
}
}
How to use
public User findUser(Long id) {
User user = repository.findById(id);
if (user == null) {
throw new UserNotFoundException("User not found: " + id);
}
return user;
}
// Handling
try {
findUser(1L);
} catch (UserNotFoundException e) {
System.out.println(e.getMessage());
}
When to create checked, when unchecked
- Checked (
extends Exception) - if error is expected and recoverable - Unchecked (
extends RuntimeException) - for business errors and programming errors
Tips
- Inherit from
RuntimeExceptionin most cases - Give meaningful names:
UserNotFoundException,OrderAlreadyShippedException - Call
super(message)to pass the message
When NOT to create custom exceptions
- Standard is sufficient -
IllegalArgumentException("Email invalid")is readable - Exception used only once - no point creating a class for a single throw
- “Parallel hierarchy” - don’t create an Exception for every new use case
Middle Level
Exception as DTO
In distributed systems, a custom exception serves as DTO for error transmission:
public class BusinessException extends RuntimeException {
private final ErrorCode code;
private final Map<String, Object> context = new HashMap<>();
public BusinessException(ErrorCode code, String message) {
super(message);
this.code = code;
}
public BusinessException with(String key, Object value) {
this.context.put(key, value);
return this;
}
public ErrorCode getCode() { return code; }
public Map<String, Object> getContext() { return context; }
}
Usage:
throw new BusinessException(INSUFFICIENT_FUNDS, "Low balance")
.with("userId", 123)
.with("balance", 0.50);
// Advantage of .with(): you can add context at the throw site, // without creating a constructor with 10 parameters. // Lambda-style: throw new BusinessException(code, msg).with(“key”, value)
Domain-Driven Exceptions
In quality architecture, exceptions are divided into levels:
- Infrastructure:
DatabaseException,NetworkException - Domain:
InsufficientFundsException,ProductOutOfStockException
Domain exceptions should be informative - not just “error”, but “user X could not buy product Y”.
The microservices problem
If service A throws OrderNotFoundException, and service B doesn’t have this class in classpath - NoClassDefFoundError.
Solution: at microservice boundaries, exceptions are translated to standard structures (RFC 7807 Problem Details). Custom exceptions live only inside the service.
RFC 7807 - standard JSON format for error messages in HTTP APIs. Defines fields: type, title, status, detail, instance. Spring supports it via ProblemDetail (since Java 17).
Senior Level
Immutable & Stackless Exceptions for Highload
In Highload systems, creating Throwable is expensive due to fillInStackTrace():
public class ValidationException extends RuntimeException {
public ValidationException(String message) {
super(message, null, false, false); // writableStackTrace = false
}
}
Thousands of exceptions per second without CPU load.
Serialization UID
Always declare serialVersionUID:
public class BusinessException extends RuntimeException {
private static final long serialVersionUID = 1L;
// ...
}
Without it, when you change the class (add a field) and deserialize an old version from Redis cache - InvalidClassException.
Exception Masking and PII
Custom exception message must not expose sensitive data:
// BAD - password in logs
throw new AuthException("Failed login for user: " + username + " with password: " + password);
// GOOD
throw new AuthException("Authentication failed for user: " + username);
Global Error Handler in Spring Boot
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusiness(BusinessException e) {
return ResponseEntity.status(400)
.body(ErrorResponse.of(e.getCode(), e.getMessage(), e.getContext()));
}
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(UserNotFoundException e) {
return ResponseEntity.status(404)
.body(ErrorResponse.of("USER_NOT_FOUND", e.getMessage()));
}
}
Diagnostics
- Log Correlation - exception message should contain data for filtering in ELK
- Error Codes - each exception should have a unique code (e.g.,
ERR-001) for localization - Micrometer - count each custom exception via metrics
Interview Cheat Sheet
Must know:
- Custom exception = regular class inheriting from
ExceptionorRuntimeException - Inherit from
RuntimeExceptionin most cases (less boilerplate) extends Exception- if error is expected and caller can recover- In microservices, custom exceptions live only inside the service; at boundary - translate to standard formats (RFC 7807)
- For highload use
writableStackTrace = falseto avoid expensivefillInStackTrace() - Always declare
serialVersionUIDfor Serializable compatibility
Frequent follow-up questions:
- When NOT to create a custom exception? - If standard is sufficient (
IllegalArgumentException), or exception is used once - How to pass context in an exception? - Add fields (
orderId,errorCode) or use fluent method.with(key, value) - What is Exception Masking? - Exception must not expose sensitive data (passwords, PII) in message
- How to handle custom exceptions in Spring? - Via
@RestControllerAdvice+@ExceptionHandler-> mapping to HTTP statuses
Red flags (NOT to say):
- “I create an exception for each use case” - This leads to “parallel hierarchy” and code bloat
- “I pass password/PII in exception message” - Security violation (PCI DSS, GDPR)
- “Custom exceptions automatically map to HTTP” - Need
@ControllerAdvice, otherwise it’s 500
Related topics:
- [[14. When should you create your own exceptions]]
- [[15. What is better extend Exception or RuntimeException]]
- [[17. How to properly log exceptions]]
- [[2. What is a checked exception and when to use it]]
- [[3. What is an unchecked exception (Runtime Exception)]]