Exception Handling Best Practices: From try‑catch to Custom Exceptions
This article explores Java exception handling from basic try‑catch‑finally syntax, through multi‑catch and try‑with‑resources, to designing custom business exceptions, offering concrete code examples, logging tips, global handlers, and performance considerations for robust backend development.
Introduction
Exception handling is a core topic in Java development; many developers over‑use try‑catch, ignoring the art of proper error management. A well‑designed exception strategy improves robustness, maintainability, and user experience.
1. Basics: try‑catch‑finally
1.1 Exception hierarchy
Java exceptions inherit from Throwable, which splits into Error (system‑level, usually ignored) and Exception. Exception further divides into checked exceptions (must be declared or caught) and unchecked RuntimeException (e.g., NullPointerException).
1.2 Simple try‑catch
Example of handling a number‑format error:
public int parseUserInput(String input) {
try {
return Integer.parseInt(input);
} catch (NumberFormatException e) {
System.out.println("输入不是有效的数字:" + input);
return 0;
}
}In production, a logging framework should record the exception instead of printing the stack trace.
1.3 finally semantics
The finally block runs regardless of whether an exception occurs, typically for resource cleanup. When both try and finally contain return, the finally return wins:
public int testReturn() {
try { return 1; }
finally { return 2; }
}Similarly, an exception thrown in finally overrides any exception from try.
try {
throw new RuntimeException("try中的异常");
} finally {
throw new RuntimeException("finally中的异常");
}2. Multi‑catch
2.1 Introduction
Before Java 7, handling multiple exception types required separate catch blocks, leading to duplicated code.
try {
// code that may throw several exceptions
} catch (IOException e) {
logger.error("IO错误", e);
} catch (SQLException e) {
logger.error("数据库错误", e);
} catch (Exception e) {
logger.error("未知错误", e);
}Java 7 allows a single block to catch several types:
try {
// code that may throw several exceptions
} catch (IOException | SQLException e) {
logger.error("系统错误", e);
}The caught types must be unrelated (no inheritance relationship). The exception variable is implicitly final, so reassignment is illegal:
try {
// code
} catch (IOException | SQLException e) {
e = new RuntimeException("重新赋值"); // compile error
}When needed, instanceof can distinguish the actual type:
catch (IOException | SQLException e) {
if (e instanceof SQLException) {
((SQLException) e).getSQLState();
} else if (e instanceof IOException) {
((IOException) e).getMessage();
}
}3. Try‑with‑resources
3.1 Problems with manual finally
Traditional resource cleanup requires null checks and can mask the original exception if close() throws.
public String readFile(String path) throws IOException {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(path));
return reader.readLine();
} finally {
if (reader != null) {
reader.close(); // may hide original exception
}
}
}3.2 Syntax
Java 7’s try‑with‑resources automatically closes any AutoCloseable resource.
public String readFile(String path) throws IOException {
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
return reader.readLine();
}
}If close() throws, the original exception is suppressed and can be retrieved via getSuppressed().
3.3 Multiple resources
try (InputStream in = new FileInputStream("input.txt");
OutputStream out = new FileOutputStream("output.txt")) {
byte[] buffer = new byte[1024];
int length;
while ((length = in.read(buffer)) != -1) {
out.write(buffer, 0, length);
}
}Resources are closed in reverse declaration order.
3.4 Combining with catch/finally
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
return reader.readLine();
} catch (IOException e) {
logger.error("读取文件失败", e);
return null;
} finally {
// additional cleanup if needed
}4. Custom Exceptions
4.1 Why custom?
Built‑in exceptions (e.g., NullPointerException) cannot express business‑level errors such as “order not found” or “email already registered.” Custom exceptions provide clear semantics, enable module‑level handling, and can carry business data.
4.2 Design principles
Choose the base class based on handling requirements: extend Exception for checked business errors, or RuntimeException for unchecked ones. In most web applications, unchecked custom exceptions are preferred.
// Business exception base class
public class BusinessException extends RuntimeException {
private final String code;
private final Map<String, Object> data;
public BusinessException(String code, String message) {
super(message);
this.code = code;
this.data = new HashMap<>();
}
public BusinessException(String code, String message, Map<String, Object> data) {
super(message);
this.code = code;
this.data = data;
}
public String getCode() { return code; }
public Map<String, Object> getData() { return data; }
}
// Specific business exceptions
public class UserNotFoundException extends BusinessException {
public UserNotFoundException(Long userId) {
super("USER_NOT_FOUND", "用户不存在", Map.of("userId", userId));
}
}
public class DuplicateEmailException extends BusinessException {
public DuplicateEmailException(String email) {
super("DUPLICATE_EMAIL", "邮箱已被注册", Map.of("email", email));
}
}4.3 Exception chaining
When re‑throwing a higher‑level exception, preserve the original cause:
public void processOrder(Long orderId) {
try {
Order order = orderRepository.findById(orderId);
if (order == null) {
throw new OrderNotFoundException(orderId);
}
paymentService.process(order);
} catch (PaymentException e) {
throw new OrderProcessingException("订单处理失败", e);
}
}5. Best‑Practice Guidelines
5.1 Core principles
Catch only exceptions you can handle; avoid empty or overly broad catch blocks.
“Throw early, catch late”: detect and throw near the error source; catch where you can meaningfully handle it.
Provide complete context in exception messages without leaking sensitive data.
5.2 Logging tips
// Bad: incomplete log
catch (SQLException e) {
logger.error("数据库查询失败");
}
// Good: full context
catch (SQLException e) {
logger.error("查询用户订单失败,用户ID:{},订单ID:{}", userId, orderId, e);
}Log an exception only once—prefer a centralized handler for subsequent layers.
5.3 Global exception handling
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public Result<?> handleBusinessException(BusinessException e) {
return Result.error(e.getCode(), e.getMessage(), e.getData());
}
@ExceptionHandler(Exception.class)
public Result<?> handleException(Exception e) {
logger.error("系统异常", e);
return Result.error("SYSTEM_ERROR", "系统繁忙,请稍后重试");
}
}5.4 Performance considerations
Creating exception objects is costly; avoid using exceptions for normal control flow, such as email validation.
// Bad: use exception for validation
public boolean isValidEmail(String email) {
try {
Pattern.matches("^[\\w.]+@[\\w.]+\\.\\w+$", email);
return true;
} catch (Exception e) {
return false;
}
}
// Good: return boolean directly
public boolean isValidEmail(String email) {
return email != null && Pattern.matches("^[\\w.]+@[\\w.]+\\.\\w+$", email);
}Conclusion
Effective Java exception handling combines proper use of try‑catch‑finally, multi‑catch, try‑with‑resources, and well‑designed custom exceptions. Applying the outlined principles, logging practices, and global handlers yields robust, maintainable backend systems.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Tech Workshop
Focused on Java backend technologies, sharing fundamentals, multithreading, JVM, the Spring ecosystem, microservices, distributed systems, high concurrency, source‑code analysis, and practical experience. Continuously delivers high‑quality original content, interview guides, and learning roadmaps to help Java developers progress from beginner to advanced, enhancing technical skills and core competitiveness.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
